//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                             emitArm64.cpp                                 XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#include "jitpch.h"
#ifdef _MSC_VER
#pragma hdrstop
#endif

#if defined(_TARGET_ARM64_)

/*****************************************************************************/
/*****************************************************************************/

#include "instr.h"
#include "emit.h"
#include "codegen.h"

/* static */ bool emitter::strictArmAsm = true;

/*****************************************************************************/

const instruction       emitJumpKindInstructions[] =
{
    INS_nop,

    #define JMP_SMALL(en, rev, ins, condcode) INS_##ins,
    #include "emitjmps.h"
};

const emitJumpKind      emitReverseJumpKinds[] =
{
    EJ_NONE,

    #define JMP_SMALL(en, rev, ins, condcode) EJ_##rev,
    #include "emitjmps.h"
};

/*****************************************************************************
 * Look up the instruction for a jump kind
 */

/*static*/ instruction      emitter::emitJumpKindToIns(emitJumpKind jumpKind)
{
    assert((unsigned)jumpKind < ArrLen(emitJumpKindInstructions));
    return emitJumpKindInstructions[jumpKind];
}

/*****************************************************************************
 * Reverse the conditional jump
 */

/*static*/ emitJumpKind     emitter::emitReverseJumpKind(emitJumpKind jumpKind)
{
    assert(jumpKind < EJ_COUNT);
    return emitReverseJumpKinds[jumpKind];
}

/*****************************************************************************
 *
 *  Return the allocated size (in bytes) of the given instruction descriptor.
 */

size_t              emitter::emitSizeOfInsDsc(instrDesc *id)
{
    assert (!emitIsTinyInsDsc(id));

    if  (emitIsScnsInsDsc(id))
        return SMALL_IDSC_SIZE;

    assert((unsigned)id->idInsFmt() < emitFmtCount);

    ID_OPS idOp         = (ID_OPS) emitFmtToOps[id->idInsFmt()];
    bool   isCallIns    = (id->idIns() == INS_bl) || (id->idIns() == INS_blr);
    bool   maybeCallIns = (id->idIns() == INS_b)  || (id->idIns() == INS_br);

    // A call instruction (ID_OP_CALL) may use a "fat" call descriptor
    // A local call to a label (i.e. call to a finally) cannot use a fat" call descriptor

    switch (idOp)
    {
    case ID_OP_NONE:
        break;

    case ID_OP_JMP:
        return  sizeof(instrDescJmp);

    case ID_OP_CALL:
        assert(isCallIns || maybeCallIns);
        if  (id->idIsLargeCall())
        {
            /* Must be a "fat" call descriptor */
            return  sizeof(instrDescCGCA);
        }
        else
        {
            assert(!id->idIsLargeDsp());
            assert(!id->idIsLargeCns());
            return sizeof(instrDesc);
        }
        break;

    default:
        NO_WAY("unexpected instruction descriptor format");
        break;
    }

    if (id->idIsLargeCns())
    {
        if (id->idIsLargeDsp())
            return sizeof(instrDescCnsDsp);
        else
            return sizeof(instrDescCns);
    }
    else
    {
        if (id->idIsLargeDsp())
            return sizeof(instrDescDsp);
        else
            return sizeof(instrDesc);
    }
}

#ifdef  DEBUG
/*****************************************************************************
 *
 *  The following called for each recorded instruction -- use for debugging.
 */
void                emitter::emitInsSanityCheck(instrDesc *id)
{
    /* What instruction format have we got? */

    switch (id->idInsFmt())
    {
        instruction ins;
        emitAttr  elemsize;
        emitAttr  datasize;
        emitAttr  dstsize;
        emitAttr  srcsize;
        ssize_t   imm;
        unsigned  immShift;
        ssize_t   index;
        ssize_t   index2;

    case IF_BI_0A:    // BI_0A   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
        break;

    case IF_BI_0B:    // BI_0B   ......iiiiiiiiii iiiiiiiiiiii....               simm19:00
        break;

    case IF_BI_0C:    // BI_0C   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
        break;

    case IF_BI_1A:    // BI_1A   ......iiiiiiiiii iiiiiiiiiiittttt      Rt       simm19:00
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_BI_1B:    // BI_1B   B.......bbbbbiii iiiiiiiiiiittttt      Rt imm6, simm14:00
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_BR_1A:    // BR_1A   ................ ......nnnnn.....         Rn
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_LS_1A:    // LS_1A   .X......iiiiiiii iiiiiiiiiiittttt      Rt    PC imm(1MB)
        assert(isGeneralRegister(id->idReg1()) ||
               isVectorRegister(id->idReg1()));
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_LS_2A:    // LS_2A   .X.......X...... ......nnnnnttttt      Rt Rn
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()));      // SP
        assert(emitGetInsSC(id) == 0);
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_LS_2B:    // LS_2B   .X.......Xiiiiii iiiiiinnnnnttttt      Rt Rn    imm(0-4095)
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()));      // SP
        assert(isValidUimm12(emitGetInsSC(id)));
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_LS_2C:    // LS_2C   .X.......X.iiiii iiiiPPnnnnnttttt      Rt Rn    imm(-256..+255) no/pre/post inc
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()));      // SP
        assert(emitGetInsSC(id) >= -0x100);
        assert(emitGetInsSC(id) <   0x100);
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        break;

    case IF_LS_3A:    // LS_3A   .X.......X.mmmmm oooS..nnnnnttttt      Rt Rn Rm ext(Rm) LSL {}
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()));      // SP
        if (id->idIsLclVar())
        {
            assert(isGeneralRegister(codeGen->rsGetRsvdReg()));
        }
        else
        {
            assert(isGeneralRegister(id->idReg3()));
        }
        assert(insOptsLSExtend(id->idInsOpt()));
        break;

    case IF_LS_3B:    // LS_3B   X............... .aaaaannnnnttttt      Rt Ra Rn
        assert((isValidGeneralDatasize(id->idOpSize()) && isIntegerRegister(id->idReg1())) ||
               (isValidVectorLSPDatasize(id->idOpSize()) && isVectorRegister(id->idReg1())));
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()) ||     // ZR
               isVectorRegister(id->idReg2()));
        assert(isIntegerRegister(id->idReg3()));      // SP
        assert(emitGetInsSC(id) == 0);
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_LS_3C:    // LS_3C   X.........iiiiii iaaaaannnnnttttt      Rt Ra Rn imm(im7,sh)
        assert((isValidGeneralDatasize(id->idOpSize()) && isIntegerRegister(id->idReg1())) ||
               (isValidVectorLSPDatasize(id->idOpSize()) && isVectorRegister(id->idReg1())));
        assert(isIntegerRegister(id->idReg1()) ||     // ZR
               isVectorRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()) ||     // ZR
               isVectorRegister(id->idReg2()));
        assert(isIntegerRegister(id->idReg3()));      // SP
        assert(emitGetInsSC(id) >= -0x40);
        assert(emitGetInsSC(id) <   0x40);
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        break;

    case IF_DI_1A:    // DI_1A   X.......shiiiiii iiiiiinnnnn.....         Rn    imm(i12,sh)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidUimm12(emitGetInsSC(id)));
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL12(id->idInsOpt()));
        break;

    case IF_DI_1B:    // DI_1B   X........hwiiiii iiiiiiiiiiiddddd      Rd       imm(i16,hw)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidImmHWVal(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DI_1C:    // DI_1C   X........Nrrrrrr ssssssnnnnn.....         Rn    imm(N,r,s)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidImmNRS(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DI_1D:    // DI_1D   X........Nrrrrrr ssssss.....ddddd      Rd       imm(N,r,s)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isValidImmNRS(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DI_1E:    // DI_1E   .ii.....iiiiiiii iiiiiiiiiiiddddd      Rd       simm21
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_DI_1F:    // DI_1F   X..........iiiii cccc..nnnnn.nzcv      Rn imm5  nzcv cond
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidImmCondFlagsImm5(emitGetInsSC(id)));
        break;

    case IF_DI_2A:    // DI_2A   X.......shiiiiii iiiiiinnnnnddddd      Rd Rn    imm(i12,sh)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isIntegerRegister(id->idReg2()));      // SP
        assert(isValidUimm12(emitGetInsSC(id)));
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL12(id->idInsOpt()));
        break;

    case IF_DI_2B:    // DI_2B   X.........Xnnnnn ssssssnnnnnddddd      Rd Rn    imm(0-63)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DI_2C:    // DI_2C   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imm(N,r,s)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmNRS(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DI_2D:    // DI_2D   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imr, imms   (N,r,s)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmNRS(emitGetInsSC(id), id->idOpSize()));
        break;

    case IF_DR_1D:    // DR_1D   X............... cccc.......ddddd      Rd       cond
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isValidImmCond(emitGetInsSC(id)));
        break;

    case IF_DR_2A:    // DR_2A   X..........mmmmm ......nnnnn.....         Rn Rm
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_DR_2B:    // DR_2B   X.......sh.mmmmm ssssssnnnnn.....         Rn Rm {LSL,LSR,ASR,ROR} imm(0-63)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // ZR
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        if (!insOptsNone(id->idInsOpt()))
        {
            if (id->idIns() == INS_tst)     // tst allows ROR, cmp/cmn don't
            {
                assert(insOptsAnyShift(id->idInsOpt()));
            }
            else
            {
                assert(insOptsAluShift(id->idInsOpt()));
            }
        }
        assert(insOptsNone(id->idInsOpt()) || (emitGetInsSC(id) > 0));
        break;

    case IF_DR_2C:    // DR_2C   X..........mmmmm ooosssnnnnn.....         Rn Rm ext(Rm) LSL imm(0-4)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP   
        assert(isGeneralRegister(id->idReg2()));
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL(id->idInsOpt()) || insOptsAnyExtend(id->idInsOpt()));
        assert(emitGetInsSC(id) >= 0);
        assert(emitGetInsSC(id) <= 4);
        if (insOptsLSL(id->idInsOpt()))
        {
            assert(emitGetInsSC(id) > 0);
        }
        break;

    case IF_DR_2D:    // DR_2D   X..........nnnnn cccc..nnnnnmmmmm      Rd Rn    cond
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmCond(emitGetInsSC(id)));
        break;

    case IF_DR_2E:    // DR_2E   X..........mmmmm ...........ddddd      Rd    Rm
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isIntegerRegister(id->idReg2()));      // ZR
        break;

    case IF_DR_2F:    // DR_2F   X.......sh.mmmmm ssssss.....ddddd      Rd    Rm {LSL,LSR,ASR} imm(0-63)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        assert(insOptsNone(id->idInsOpt()) || insOptsAluShift(id->idInsOpt()));
        assert(insOptsNone(id->idInsOpt()) || (emitGetInsSC(id) > 0));
        break;

    case IF_DR_2G:    // DR_2G   X............... ......nnnnnddddd      Rd    Rm
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isIntegerRegister(id->idReg2()));      // SP
        break;

    case IF_DR_2H:    // DR_2H   X........X...... ......nnnnnddddd      Rd Rn
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_DR_2I:    // DR_2I   X..........mmmmm cccc..nnnnn.nzcv      Rn Rm    nzcv cond
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isValidImmCondFlags(emitGetInsSC(id)));
        break;

    case IF_DR_3A:    // DR_3A   X..........mmmmm ......nnnnnmmmmm      Rd Rn Rm
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isIntegerRegister(id->idReg2()));      // SP
        if (id->idIsLclVar())
        {
            assert(isGeneralRegister(codeGen->rsGetRsvdReg()));
        }
        else
        {
            assert(isGeneralRegister(id->idReg3()));
        }
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_DR_3B:    // DR_3B   X.......sh.mmmmm ssssssnnnnnddddd      Rd Rn Rm {LSL,LSR,ASR,ROR} imm(0-63)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        assert(insOptsNone(id->idInsOpt()) || insOptsAnyShift(id->idInsOpt()));
        assert(insOptsNone(id->idInsOpt()) || (emitGetInsSC(id) > 0));
        break;

    case IF_DR_3C:    // DR_3C   X..........mmmmm ooosssnnnnnddddd      Rd Rn Rm ext(Rm) LSL imm(0-4)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isIntegerRegister(id->idReg1()));      // SP
        assert(isIntegerRegister(id->idReg2()));      // SP
        assert(isGeneralRegister(id->idReg3()));
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL(id->idInsOpt()) || insOptsAnyExtend(id->idInsOpt()));
        assert(emitGetInsSC(id) >= 0);
        assert(emitGetInsSC(id) <= 4);
        if (insOptsLSL(id->idInsOpt()))
        {
            assert(emitGetInsSC(id) > 0);
        }
        break;

    case IF_DR_3D:    // DR_3D   X..........mmmmm cccc..nnnnnmmmmm      Rd Rn Rm cond
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(isValidImmCond(emitGetInsSC(id)));
        break;

    case IF_DR_3E:    // DR_3E   X........X.mmmmm ssssssnnnnnddddd      Rd Rn Rm imm(0-63)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(isValidImmShift(emitGetInsSC(id), id->idOpSize()));
        assert(insOptsNone(id->idInsOpt()));
        break;

    case IF_DR_4A:    // DR_4A   X..........mmmmm .aaaaannnnnddddd      Rd Rn Rm Ra
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(isGeneralRegister(id->idReg4()));
        break;

    case IF_DV_1A:    // DV_1A   .........X.iiiii iii........ddddd      Vd imm8    (fmov - immediate scalar)
        assert(insOptsNone(id->idInsOpt()));
        elemsize = id->idOpSize();
        assert(isValidVectorElemsizeFloat(elemsize));
        assert(isVectorRegister(id->idReg1()));
        assert(isValidUimm8(emitGetInsSC(id)));
        break;

    case IF_DV_1B:    // DV_1B   .QX..........iii cmod..iiiiiddddd      Vd imm8    (immediate vector)
        ins      = id->idIns();
        imm      = emitGetInsSC(id) & 0x0ff;
        immShift = (emitGetInsSC(id) & 0x700) >> 8;
        assert(immShift >= 0);
        datasize = id->idOpSize();
        assert(isValidVectorDatasize(datasize));
        assert(isValidArrangement(datasize, id->idInsOpt()));
        elemsize = optGetElemsize(id->idInsOpt());
        if (ins == INS_fmov) 
        {
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(id->idInsOpt() != INS_OPTS_1D);    // Reserved encoding
            assert(immShift == 0);
        }
        else 
        {
            assert(isValidVectorElemsize(elemsize));
            assert((immShift != 4) && (immShift != 7));  // always invalid values 
            if (ins != INS_movi)   // INS_mvni, INS_orr, INS_bic
            {
                assert((elemsize != EA_1BYTE) && (elemsize != EA_8BYTE));  // only H or S
                if (elemsize == EA_2BYTE)
                {
                    assert(immShift < 2);
                }
                else // (elemsize == EA_4BYTE)
                {
                    if (ins != INS_mvni)
                    {
                        assert(immShift < 4);
                    }
                }
            }
        }
        assert(isVectorRegister(id->idReg1()));
        assert(isValidUimm8(imm));
        break;

    case IF_DV_1C:    // DV_1C   .........X...... ......nnnnn.....      Vn #0.0    (fcmp - with zero)
        assert(insOptsNone(id->idInsOpt()));
        elemsize = id->idOpSize();
        assert(isValidVectorElemsizeFloat(elemsize));
        assert(isVectorRegister(id->idReg1()));
        break;

    case IF_DV_2A:    // DV_2A   .Q.......X...... ......nnnnnddddd      Vd Vn      (fabs, fcvt - vector)
    case IF_DV_2M:    // DV_2M   .Q......XX...... ......nnnnnddddd      Vd Vn      (abs, neg   - vector)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2N:    // DV_2N   .........iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - scalar)
        assert(id->idOpSize() == EA_8BYTE);
        assert(insOptsNone(id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isValidImmShift(emitGetInsSC(id), EA_8BYTE));
        break;

    case IF_DV_2O:    // DV_2O   .Q.......iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - vector)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        elemsize = optGetElemsize(id->idInsOpt());
        assert(isValidImmShift(emitGetInsSC(id), elemsize));
        break;

    case IF_DV_2B:    // DV_2B   .Q.........iiiii ......nnnnnddddd      Rd Vn[]  (umov/smov    - to general)
        elemsize = id->idOpSize();
        index = emitGetInsSC(id);
        assert(insOptsNone(id->idInsOpt()));
        assert(isValidVectorIndex(EA_16BYTE, elemsize, index));
        assert(isValidVectorElemsize(elemsize));
        assert(isGeneralRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2C:    // DV_2C   .Q.........iiiii ......nnnnnddddd      Vd Rn    (dup/ins - vector from general)
        if (id->idIns() == INS_dup) 
        {
            datasize = id->idOpSize();
            assert(isValidVectorDatasize(datasize));
            assert(isValidArrangement(datasize, id->idInsOpt()));
            elemsize = optGetElemsize(id->idInsOpt());
        }
        else  // INS_ins
        {
            datasize = EA_16BYTE;
            elemsize = id->idOpSize();
            assert(isValidVectorElemsize(elemsize));
        }
        assert(isVectorRegister(id->idReg1()));
        assert(isGeneralRegisterOrZR(id->idReg2()));
        break;

    case IF_DV_2D:    // DV_2D   .Q.........iiiii ......nnnnnddddd      Vd Vn[]  (dup - vector)
        datasize = id->idOpSize();
        assert(isValidVectorDatasize(datasize));
        assert(isValidArrangement(datasize, id->idInsOpt()));
        elemsize = optGetElemsize(id->idInsOpt());
        index = emitGetInsSC(id);
        assert(isValidVectorIndex(datasize, elemsize, index));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2E:    // DV_2E   ...........iiiii ......nnnnnddddd      Vd Vn[]  (dup - scalar)
        elemsize = id->idOpSize();
        index = emitGetInsSC(id);
        assert(isValidVectorIndex(EA_16BYTE, elemsize, index));
        assert(isValidVectorElemsize(elemsize));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2F:    // DV_2F   ...........iiiii .jjjj.nnnnnddddd      Vd[] Vn[] (ins - element)
        imm = emitGetInsSC(id);
        index  = (imm >> 4) & 0xf;
        index2 = imm & 0xf;
        elemsize = id->idOpSize();
        assert(isValidVectorElemsize(elemsize));
        assert(isValidVectorIndex(EA_16BYTE, elemsize, index));
        assert(isValidVectorIndex(EA_16BYTE, elemsize, index2));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2L:    // DV_2L   ........XX...... ......nnnnnddddd      Vd Vn      (abs, neg - scalar)
        assert(id->idOpSize() == EA_8BYTE);    // only type D is supported
        __fallthrough;

    case IF_DV_2G:    // DV_2G   .........X...... ......nnnnnddddd      Vd Vn      (fmov, fcvtXX - register)
    case IF_DV_2K:    // DV_2K   .........X.mmmmm ......nnnnn.....      Vn Vm      (fcmp)
        assert(insOptsNone(id->idInsOpt()));
        assert(isValidVectorElemsizeFloat(id->idOpSize()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2H:    // DV_2H   X........X...... ......nnnnnddddd      Rd Vn      (fmov/fcvtXX - to general)
        assert(insOptsConvertFloatToInt(id->idInsOpt()));
        dstsize = optGetDstsize(id->idInsOpt());
        srcsize = optGetSrcsize(id->idInsOpt());
        assert(isValidGeneralDatasize(dstsize));
        assert(isValidVectorElemsizeFloat(srcsize));
        assert(dstsize == id->idOpSize());
        assert(isGeneralRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_2I:    // DV_2I   X........X...... ......nnnnnddddd      Vd Rn      (fmov/Xcvtf - from general)
        assert(insOptsConvertIntToFloat(id->idInsOpt()));
        dstsize = optGetDstsize(id->idInsOpt());
        srcsize = optGetSrcsize(id->idInsOpt());
        assert(isValidGeneralDatasize(srcsize));
        assert(isValidVectorElemsizeFloat(dstsize));
        assert(dstsize == id->idOpSize());
        assert(isVectorRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_DV_2J:    // DV_2J   ........SS.....D D.....nnnnnddddd      Vd Vn      (fcvt)
        assert(insOptsConvertFloatToFloat(id->idInsOpt()));
        dstsize = optGetDstsize(id->idInsOpt());
        srcsize = optGetSrcsize(id->idInsOpt());
        assert(isValidVectorFcvtsize(srcsize));
        assert(isValidVectorFcvtsize(dstsize));
        assert(dstsize == id->idOpSize());
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        break;

    case IF_DV_3A:    // DV_3A   .Q......XX.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        elemsize = optGetElemsize(id->idInsOpt());
        ins      = id->idIns();
        if (ins == INS_mul)
        {
            assert(elemsize != EA_8BYTE);    // can't use 2D or 1D
        }
        else if (ins == INS_pmul)
        {
            assert(elemsize == EA_1BYTE);    // only supports 8B or 16B
        }
        break;

    case IF_DV_3AI:   // DV_3AI  .Q......XXLMmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        elemsize = optGetElemsize(id->idInsOpt());
        assert(isValidVectorIndex(EA_16BYTE, elemsize, emitGetInsSC(id)));
        // Only has encodings for H or S elemsize
        assert((elemsize == EA_2BYTE) || (elemsize == EA_4BYTE)); 
        break;

    case IF_DV_3B:    // DV_3B   .Q.......X.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        break;

    case IF_DV_3BI:   // DV_3BI  .Q.......XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        elemsize = optGetElemsize(id->idInsOpt());
        assert(isValidVectorIndex(id->idOpSize(), elemsize, emitGetInsSC(id)));
        break;

    case IF_DV_3C:    // DV_3C   .Q.........mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        assert(isValidVectorDatasize(id->idOpSize()));
        assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        break;

    case IF_DV_3D:    // DV_3D   .........X.mmmmm ......nnnnnddddd      Vd Vn Vm   (scalar)
        assert(isValidScalarDatasize(id->idOpSize()));
        assert(insOptsNone(id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        break;

    case IF_DV_3DI:   // DV_3DI  .........XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (scalar by elem)
        assert(isValidScalarDatasize(id->idOpSize()));
        assert(insOptsNone(id->idInsOpt()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        elemsize = id->idOpSize();
        assert(isValidVectorIndex(EA_16BYTE, elemsize, emitGetInsSC(id)));
        break;

    case IF_DV_3E:    // DV_3E   ...........mmmmm ......nnnnnddddd      Vd Vn Vm  (scalar)
        assert(insOptsNone(id->idInsOpt()));
        assert(id->idOpSize() == EA_8BYTE);
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        break;

    case IF_DV_4A:    // DR_4A   .........X.mmmmm .aaaaannnnnddddd      Rd Rn Rm Ra (scalar)
        assert(isValidGeneralDatasize(id->idOpSize()));
        assert(isVectorRegister(id->idReg1()));
        assert(isVectorRegister(id->idReg2()));
        assert(isVectorRegister(id->idReg3()));
        assert(isVectorRegister(id->idReg4()));
        break;

    case IF_SN_0A:    // SN_0A   ................ ................      
    case IF_SI_0A:    // SI_0A   ...........iiiii iiiiiiiiiii.....               imm16
    case IF_SI_0B:    // SI_0B   ................ ....bbbb........               imm4 - barrier
        break;

    default:
        printf("unexpected format %s\n", emitIfName(id->idInsFmt()));
        assert(!"Unexpected format");
        break;
    }
}
#endif  // DEBUG

bool           emitter::emitInsMayWriteToGCReg(instrDesc *id)
{
    instruction     ins  = id->idIns();
    insFormat       fmt  = id->idInsFmt();

    switch (fmt)
    {

    // These are the formats with "destination" registers:

    case IF_DI_1B:       // DI_1B   X........hwiiiii iiiiiiiiiiiddddd      Rd       imm(i16,hw)
    case IF_DI_1D:       // DI_1D   X........Nrrrrrr ssssss.....ddddd      Rd       imm(N,r,s)
    case IF_DI_1E:       // DI_1E   .ii.....iiiiiiii iiiiiiiiiiiddddd      Rd       simm21

    case IF_DI_2A:       // DI_2A   X.......shiiiiii iiiiiinnnnnddddd      Rd Rn    imm(i12,sh)
    case IF_DI_2B:       // DI_2B   X.........Xnnnnn ssssssnnnnnddddd      Rd Rn    imm(0-63)
    case IF_DI_2C:       // DI_2C   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imm(N,r,s)
    case IF_DI_2D:       // DI_2D   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imr, imms   (N,r,s)

    case IF_DR_1D:       // DR_1D   X............... cccc.......ddddd      Rd       cond

    case IF_DR_2D:       // DR_2D   X..........nnnnn cccc..nnnnnddddd      Rd Rn    cond
    case IF_DR_2E:       // DR_2E   X..........mmmmm ...........ddddd      Rd    Rm
    case IF_DR_2F:       // DR_2F   X.......sh.mmmmm ssssss.....ddddd      Rd    Rm {LSL,LSR,ASR} imm(0-63)
    case IF_DR_2G:       // DR_2G   X............... ......nnnnnddddd      Rd Rn    
    case IF_DR_2H:       // DR_2H   X........X...... ......nnnnnddddd      Rd Rn

    case IF_DR_3A:       // DR_3A   X..........mmmmm ......nnnnnddddd      Rd Rn Rm
    case IF_DR_3B:       // DR_3B   X.......sh.mmmmm ssssssnnnnnddddd      Rd Rn Rm {LSL,LSR,ASR} imm(0-63)
    case IF_DR_3C:       // DR_3C   X..........mmmmm xxxsssnnnnnddddd      Rd Rn Rm ext(Rm) LSL imm(0-4)
    case IF_DR_3D:       // DR_3D   X..........mmmmm cccc..nnnnnddddd      Rd Rn Rm cond
    case IF_DR_3E:       // DR_3E   X........X.mmmmm ssssssnnnnnddddd      Rd Rn Rm imm(0-63)

    case IF_DR_4A:       // DR_4A   X..........mmmmm .aaaaannnnnddddd      Rd Rn Rm Ra

    case IF_DV_2B:       // DV_2B   .Q.........iiiii ......nnnnnddddd      Rd Vn[]    (umov - to general)
    case IF_DV_2H:       // DV_2H   X........X...... ......nnnnnddddd      Rd Vn      (fmov - to general)

        return true;

    case IF_DV_2C:       // DV_2C   .Q.........iiiii ......nnnnnddddd      Vd Rn      (dup/ins - vector from general)
    case IF_DV_2D:       // DV_2D   .Q.........iiiii ......nnnnnddddd      Vd Vn[]    (dup - vector)
    case IF_DV_2E:       // DV_2E   ...........iiiii ......nnnnnddddd      Vd Vn[]    (dup - scalar)
    case IF_DV_2F:       // DV_2F   ...........iiiii .jjjj.nnnnnddddd      Vd[] Vn[]  (ins - element)
    case IF_DV_2G:       // DV_2G   .........X...... ......nnnnnddddd      Vd Vn      (fmov, fcvtXX - register)
    case IF_DV_2I:       // DV_2I   X........X...... ......nnnnnddddd      Vd Rn      (fmov - from general)
    case IF_DV_2J:       // DV_2J   ........SS.....D D.....nnnnnddddd      Vd Vn      (fcvt)
    case IF_DV_2K:       // DV_2K   .........X.mmmmm ......nnnnn.....      Vn Vm      (fcmp)
    case IF_DV_2L:       // DV_2L   ........XX...... ......nnnnnddddd      Vd Vn      (abs, neg - scalar)
    case IF_DV_2M:       // DV_2M   .Q......XX...... ......nnnnnddddd      Vd Vn      (abs, neg - vector)
    case IF_DV_3A:       // DV_3A   .Q......XX.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
    case IF_DV_3AI:      // DV_3AI  .Q......XXLMmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector)
    case IF_DV_3B:       // DV_3B   .Q.......X.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
    case IF_DV_3BI:      // DV_3BI  .Q.......XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
    case IF_DV_3C:       // DV_3C   .Q.........mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
    case IF_DV_3D:       // DV_3D   .........X.mmmmm ......nnnnnddddd      Vd Vn Vm   (scalar)
    case IF_DV_3DI:      // DV_3DI  .........XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (scalar by elem)
    case IF_DV_3E:       // DV_3E   ...........mmmmm ......nnnnnddddd      Vd Vn Vm   (scalar)
    case IF_DV_4A:       // DV_4A   .........X.mmmmm .aaaaannnnnddddd      Vd Va Vn Vm (scalar)
        // Tracked GC pointers cannot be placed into the SIMD registers.
        return false;

    // These are the load/store formats with "target" registers:

    case IF_LS_1A:       // LS_1A   .X......iiiiiiii iiiiiiiiiiittttt      Rt    PC imm(1MB)
    case IF_LS_2A:       // LS_2A   .X.......X...... ......nnnnnttttt      Rt Rn
    case IF_LS_2B:       // LS_2B   .X.......Xiiiiii iiiiiinnnnnttttt      Rt Rn    imm(0-4095)
    case IF_LS_2C:       // LS_2C   .X.......X.iiiii iiiiP.nnnnnttttt      Rt Rn    imm(-256..+255) pre/post inc
    case IF_LS_3A:       // LS_3A   .X.......X.mmmmm xxxS..nnnnnttttt      Rt Rn Rm ext(Rm) LSL {}
    case IF_LS_3B:       // LS_3B   X............... .aaaaannnnnttttt      Rt Ra Rn
    case IF_LS_3C:       // LS_3C   X.........iiiiii iaaaaannnnnttttt      Rt Ra Rn imm(im7,sh)

        // For the Store instructions the "target" register is actually a "source" value

        if (emitInsIsStore(ins))
        {
            return false;
        }
        else
        {
            assert(emitInsIsLoad(ins));
            return true;
        }

    default:
        return false;
    }
}

bool           emitter::emitInsWritesToLclVarStackLoc(instrDesc *id)
{
    if (!id->idIsLclVar())
        return false;

    instruction     ins  = id->idIns();

    // This list is related to the list of instructions used to store local vars in emitIns_S_R().
    // We don't accept writing to float local vars.

    switch (ins)
    {
    case INS_strb: 
    case INS_strh:
    case INS_str: 
    case INS_stur:
    case INS_sturb:
    case INS_sturh:
        return true;
    default:
        return false;
    }
}

bool           emitter::emitInsMayWriteMultipleRegs(instrDesc *id)
{
    instruction     ins  = id->idIns();

    switch (ins)
    {
    case INS_ldp:
    case INS_ldpsw:
    case INS_ldnp:
        return true;
    default:
        return false;
    }
}

// Takes an instrDesc 'id' and uses the instruction 'ins' to determine the 
// size of the target register that is written or read by the instruction.
// Note that even if EA_4BYTE is returned a load instruction will still 
// always zero the upper 4 bytes of the target register.
// This method is required so that we can distinguish between loads that are
// sign-extending as they can have two different sizes for their target register.  
// Additionally for instructions like 'ldr' and 'str' these can load/store  
// either 4 byte or 8 bytes to/from the target register.
// By convention the small unsigned load instructions are considered to write
// a 4 byte sized target register, though since these also zero the upper 4 bytes 
// they could equally be considered to write the unsigned value to full 8 byte register.
//
emitAttr  emitter::emitInsTargetRegSize(instrDesc *id)
{
    instruction     ins  = id->idIns();
    emitAttr      result = EA_UNKNOWN;

    // This is used to determine the size of the target registers for a load/store instruction

    switch (ins)
    {
    case INS_ldrb:
    case INS_strb: 
    case INS_ldurb:
    case INS_sturb:
        result = EA_4BYTE;
        break;

    case INS_ldrh:
    case INS_strh:
    case INS_ldurh:
    case INS_sturh:
        result = EA_4BYTE;
        break;

    case INS_ldrsb:
    case INS_ldursb:
    case INS_ldrsh:
    case INS_ldursh:
        if (id->idOpSize() == EA_8BYTE)
            result = EA_8BYTE;
        else
            result = EA_4BYTE;
        break;

    case INS_ldrsw:
    case INS_ldursw:
    case INS_ldpsw:
        result = EA_8BYTE;
        break;

    case INS_ldp: 
    case INS_stp: 
    case INS_ldnp: 
    case INS_stnp: 
        result = id->idOpSize();
        break;

    case INS_ldr: 
    case INS_str: 
    case INS_ldur:
    case INS_stur:
        result = id->idOpSize();
        break;

    default:
        NO_WAY("unexpected instruction");
        break;
    }
    return result;
}

// Takes an instrDesc and uses the instruction to determine the 'size' of the
// data that is loaded from memory.
//
emitAttr  emitter::emitInsLoadStoreSize(instrDesc *id)
{
    instruction     ins  = id->idIns();
    emitAttr      result = EA_UNKNOWN;

    // The 'result' returned is the 'size' of the data that is loaded from memory.

    switch (ins)
    {
    case INS_ldrb:
    case INS_strb: 
    case INS_ldurb:
    case INS_sturb:
    case INS_ldrsb:
    case INS_ldursb:
        result = EA_1BYTE;
        break;

    case INS_ldrh:
    case INS_strh:
    case INS_ldurh:
    case INS_sturh:
    case INS_ldrsh:
    case INS_ldursh:
        result = EA_2BYTE;
        break;

    case INS_ldrsw:
    case INS_ldursw:
    case INS_ldpsw:
        result = EA_4BYTE;
        break;

    case INS_ldp: 
    case INS_stp: 
    case INS_ldnp: 
    case INS_stnp: 
        result = id->idOpSize();
        break;

    case INS_ldr: 
    case INS_str: 
    case INS_ldur:
    case INS_stur:
        result = id->idOpSize();
        break;

    default:
        NO_WAY("unexpected instruction");
        break;
    }
    return result;
}

/*****************************************************************************/
#ifdef  DEBUG

static const char * const  xRegNames[] =
{
    #define REGDEF(name, rnum, mask, xname, wname) xname,
    #include "register.h"
};

static const char * const  wRegNames[] =
{
    #define REGDEF(name, rnum, mask, xname, wname) wname,
    #include "register.h"
};

static const char * const  vRegNames[] =
{
    "v0",  "v1",  "v2",  "v3",  "v4", 
    "v5",  "v6",  "v7",  "v8",  "v9", 
    "v10", "v11", "v12", "v13", "v14", 
    "v15", "v16", "v17", "v18", "v19", 
    "v20", "v21", "v22", "v23", "v24", 
    "v25", "v26", "v27", "v28", "v29",
    "v30", "v31"
};

static const char * const  qRegNames[] =
{
    "q0",  "q1",  "q2",  "q3",  "q4", 
    "q5",  "q6",  "q7",  "q8",  "q9", 
    "q10", "q11", "q12", "q13", "q14", 
    "q15", "q16", "q17", "q18", "q19", 
    "q20", "q21", "q22", "q23", "q24", 
    "q25", "q26", "q27", "q28", "q29",
    "q30", "q31"
};

static const char * const  hRegNames[] =
{
    "h0",  "h1",  "h2",  "h3",  "h4", 
    "h5",  "h6",  "h7",  "h8",  "h9", 
    "h10", "h11", "h12", "h13", "h14", 
    "h15", "h16", "h17", "h18", "h19", 
    "h20", "h21", "h22", "h23", "h24", 
    "h25", "h26", "h27", "h28", "h29",
    "h30", "h31"
};
static const char * const  bRegNames[] =
{
    "b0",  "b1",  "b2",  "b3",  "b4", 
    "b5",  "b6",  "b7",  "b8",  "b9", 
    "b10", "b11", "b12", "b13", "b14", 
    "b15", "b16", "b17", "b18", "b19", 
    "b20", "b21", "b22", "b23", "b24", 
    "b25", "b26", "b27", "b28", "b29",
    "b30", "b31"
};

/*****************************************************************************
 *
 *  Return a string that represents the given register.
 */

const   char *      emitter::emitRegName(regNumber reg, 
                                         emitAttr  size,
                                         bool      varName)
{
    assert(reg < REG_COUNT);

    const char *  rn = nullptr;

    if (size == EA_8BYTE)
    {
        rn = xRegNames[reg];
    }
    else if (size == EA_4BYTE)
    {
        rn = wRegNames[reg];
    }
    else if (isVectorRegister(reg))
    {
        if (size == EA_16BYTE)
        {
            rn = qRegNames[reg - REG_V0];
        }
        else if (size == EA_2BYTE)
        {
            rn = hRegNames[reg - REG_V0];
        }
        else if (size == EA_1BYTE)
        {
            rn = bRegNames[reg- REG_V0];
        }
    }

    assert(rn != nullptr);

    return  rn;
}

/*****************************************************************************
 *
 *  Return a string that represents the given register.
 */

const   char *      emitter::emitVectorRegName(regNumber reg)
{
    assert((reg >= REG_V0) && (reg <= REG_V31));

    int index = (int) reg - (int) REG_V0;

    return  vRegNames[index];
}
#endif // DEBUG

/*****************************************************************************
 *
 *  Returns the base encoding of the given CPU instruction.
 */

emitter::insFormat  emitter::emitInsFormat(instruction ins)
{
    const static insFormat insFormats[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                ) fmt,
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) fmt,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) fmt,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) fmt,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) fmt,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) fmt,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) fmt,
        #include "instrs.h"
    };

    assert(ins < ArrLen(insFormats));
    assert((insFormats[ins] != IF_NONE));

    return  insFormats[ins];
}

// INST_FP is 1
#define   LD  2
#define   ST  4
#define   CMP 8

/*static*/ const BYTE CodeGenInterface::instInfo[] =
{
    #define INST1(id, nm, fp, ldst, fmt, e1                                ) ldst | INST_FP*fp,
    #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) ldst | INST_FP*fp,
    #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) ldst | INST_FP*fp,
    #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) ldst | INST_FP*fp,
    #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) ldst | INST_FP*fp,
    #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) ldst | INST_FP*fp,
    #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) ldst | INST_FP*fp,
    #include "instrs.h"
};


/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of compare or test instruction
 */

bool  emitter::emitInsIsCompare(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & CMP) ? true : false;
    else
        return false;
}

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of load instruction
 */

bool  emitter::emitInsIsLoad(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & LD) ? true : false;
    else
        return false;
}
/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of store instruction
 */

bool  emitter::emitInsIsStore(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & ST) ? true : false;
    else
        return false;
}

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of load/store instruction
 */

bool  emitter::emitInsIsLoadOrStore(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & (LD|ST)) ? true : false;
    else
        return false;

}

#undef  LD
#undef  ST
#undef  CMP

/*****************************************************************************
 *
 *  Returns the specific encoding of the given CPU instruction and format
 */

emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
{
    const static code_t insCodes1[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                ) e1,
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) e1,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e1,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e1,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e1,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e1,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e1,
        #include "instrs.h"
    };
    const static code_t insCodes2[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) e2,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e2,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e2,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e2,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e2,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e2,
        #include "instrs.h"
    };
    const static code_t insCodes3[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e3,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e3,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e3,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e3,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e3,
        #include "instrs.h"
    };
    const static code_t insCodes4[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e4,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e4,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e4,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e4,
        #include "instrs.h"
    };
    const static code_t insCodes5[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e5,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e5,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e5,
        #include "instrs.h"
    };
    const static code_t insCodes6[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e6,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e6,
        #include "instrs.h"
    };
    const static code_t insCodes7[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e7,
        #include "instrs.h"
    };
    const static code_t insCodes8[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e8,
        #include "instrs.h"
    };
    const static code_t insCodes9[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e9,
        #include "instrs.h"
    };
    const static insFormat formatEncode9[9]  = { IF_DR_2E,  IF_DR_2G,  IF_DI_1B,  IF_DI_1D,  IF_DV_3C,  
                                                 IF_DV_2B,  IF_DV_2C,  IF_DV_2E,  IF_DV_2F };
    const static insFormat formatEncode6A[6] = { IF_DR_3A,  IF_DR_3B,  IF_DR_3C,  IF_DI_2A,  IF_DV_3A, 
                                                 IF_DV_3E };
    const static insFormat formatEncode5A[5] = { IF_LS_2A,  IF_LS_2B,  IF_LS_2C,  IF_LS_3A,  IF_LS_1A };
    const static insFormat formatEncode5B[5] = { IF_DV_2G,  IF_DV_2H,  IF_DV_2I,  IF_DV_1A,  IF_DV_1B };
    const static insFormat formatEncode5C[5] = { IF_DR_3A,  IF_DR_3B,  IF_DI_2C,  IF_DV_3C,  IF_DV_1B };
    const static insFormat formatEncode4A[4] = { IF_LS_2A,  IF_LS_2B,  IF_LS_2C,  IF_LS_3A };
    const static insFormat formatEncode4B[4] = { IF_DR_3A,  IF_DR_3B,  IF_DR_3C,  IF_DI_2A };
    const static insFormat formatEncode4C[4] = { IF_DR_2A,  IF_DR_2B,  IF_DR_2C,  IF_DI_1A };
    const static insFormat formatEncode4D[4] = { IF_DV_3B,  IF_DV_3D,  IF_DV_3BI, IF_DV_3DI };
    const static insFormat formatEncode4E[4] = { IF_DR_3A,  IF_DR_3B,  IF_DI_2C,  IF_DV_3C };
    const static insFormat formatEncode4F[4] = { IF_DR_3A,  IF_DR_3B,  IF_DV_3C,  IF_DV_1B };
    const static insFormat formatEncode4G[4] = { IF_DR_2E,  IF_DR_2F,  IF_DV_2M,  IF_DV_2L };
    const static insFormat formatEncode3A[3] = { IF_DR_3A,  IF_DR_3B,  IF_DI_2C };
    const static insFormat formatEncode3B[3] = { IF_DR_2A,  IF_DR_2B,  IF_DI_1C };
    const static insFormat formatEncode3C[3] = { IF_DR_3A,  IF_DR_3B,  IF_DV_3C };
    const static insFormat formatEncode3D[3] = { IF_DV_2C,  IF_DV_2D,  IF_DV_2E };
    const static insFormat formatEncode3E[3] = { IF_DV_3B,  IF_DV_3BI, IF_DV_3DI };
    const static insFormat formatEncode3F[3] = { IF_DV_2A,  IF_DV_2G,  IF_DV_2H };
    const static insFormat formatEncode3G[3] = { IF_DV_2A,  IF_DV_2G,  IF_DV_2I };
    const static insFormat formatEncode3H[3] = { IF_DR_3A,  IF_DV_3A,  IF_DV_3AI };
    const static insFormat formatEncode3I[3] = { IF_DR_2E,  IF_DR_2F,  IF_DV_2M };
    const static insFormat formatEncode2A[2] = { IF_DR_2E,  IF_DR_2F };
    const static insFormat formatEncode2B[2] = { IF_DR_3A,  IF_DR_3B };
    const static insFormat formatEncode2C[2] = { IF_DR_3A,  IF_DI_2D };
    const static insFormat formatEncode2D[2] = { IF_DR_3A,  IF_DI_2B };
    const static insFormat formatEncode2E[2] = { IF_LS_3B,  IF_LS_3C };
    const static insFormat formatEncode2F[2] = { IF_DR_2I,  IF_DI_1F };
    const static insFormat formatEncode2G[2] = { IF_DV_3B,  IF_DV_3D };
    const static insFormat formatEncode2H[2] = { IF_DV_2C,  IF_DV_2F };
    const static insFormat formatEncode2I[2] = { IF_DV_2K,  IF_DV_1C };
    const static insFormat formatEncode2J[2] = { IF_DV_2A,  IF_DV_2G };
    const static insFormat formatEncode2K[2] = { IF_DV_2M,  IF_DV_2L };
    const static insFormat formatEncode2L[2] = { IF_DV_2G,  IF_DV_2M };
    const static insFormat formatEncode2M[2] = { IF_DV_3A,  IF_DV_3AI };
    const static insFormat formatEncode2N[2] = { IF_DV_2N,  IF_DV_2O };

    code_t    code   = BAD_CODE;
    insFormat insFmt = emitInsFormat(ins);
    bool      encoding_found = false;
    int       index  = -1;

    switch (insFmt)
    {
    case IF_EN9:
        for (index=0; index<9; index++)
        {
            if (fmt == formatEncode9[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN6A:
        for (index=0; index<6; index++)
        {
            if (fmt == formatEncode6A[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN5A:
        for (index=0; index<5; index++)
        {
            if (fmt == formatEncode5A[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN5B:
        for (index=0; index<5; index++)
        {
            if (fmt == formatEncode5B[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN5C:
        for (index=0; index<5; index++)
        {
            if (fmt == formatEncode5C[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4A:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4A[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4B:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4B[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4C:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4C[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4D:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4D[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4E:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4E[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4F:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4F[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN4G:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4G[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3A:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3A[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3B:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3B[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3C:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3C[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3D:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3D[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3E:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3E[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3F:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3F[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3G:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3G[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3H:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3H[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN3I:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3I[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2A:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2A[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2B:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2B[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2C:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2C[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2D:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2D[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2E:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2E[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2F:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2F[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2G:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2G[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2H:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2H[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2I:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2I[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2J:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2J[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2K:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2K[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2L:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2L[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2M:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2M[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_EN2N:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2N[index])
            {
                encoding_found = true;
                break;
            }
        }
        break;

    case IF_BI_0A:
    case IF_BI_0B:
    case IF_BI_0C:
    case IF_BI_1A:
    case IF_BI_1B:
    case IF_BR_1A:
    case IF_LS_1A:
    case IF_LS_2A:
    case IF_LS_2B:
    case IF_LS_2C:
    case IF_LS_3A:
    case IF_LS_3B:
    case IF_LS_3C:
    case IF_DI_1A:
    case IF_DI_1B:
    case IF_DI_1C:
    case IF_DI_1D:
    case IF_DI_1E:
    case IF_DI_1F:
    case IF_DI_2A:
    case IF_DI_2B:
    case IF_DI_2C:
    case IF_DI_2D:
    case IF_DR_1D:
    case IF_DR_2A:
    case IF_DR_2B:
    case IF_DR_2C:
    case IF_DR_2D:
    case IF_DR_2E:
    case IF_DR_2F:
    case IF_DR_2G:
    case IF_DR_2H:
    case IF_DR_2I:
    case IF_DR_3A:
    case IF_DR_3B:
    case IF_DR_3C:
    case IF_DR_3D:
    case IF_DR_3E:
    case IF_DR_4A:
    case IF_DV_1A:
    case IF_DV_1B:
    case IF_DV_1C:
    case IF_DV_2A:
    case IF_DV_2B:
    case IF_DV_2C:
    case IF_DV_2D:
    case IF_DV_2E:
    case IF_DV_2F:
    case IF_DV_2G:
    case IF_DV_2H:
    case IF_DV_2I:
    case IF_DV_2J:
    case IF_DV_2K:
    case IF_DV_2L:
    case IF_DV_2M:
    case IF_DV_2N:
    case IF_DV_2O:
    case IF_DV_3A:
    case IF_DV_3AI:
    case IF_DV_3B:
    case IF_DV_3BI:
    case IF_DV_3C:
    case IF_DV_3D:
    case IF_DV_3DI:
    case IF_DV_3E:
    case IF_DV_4A:
    case IF_SN_0A:
    case IF_SI_0A:
    case IF_SI_0B:

        index = 0;
        encoding_found = true;
        break;

    default:

        encoding_found = false;
        break;
    }

    assert(encoding_found);

    switch (index)
    {
    case 0:
        assert(ins < ArrLen(insCodes1));
        code = insCodes1[ins];
        break;
    case 1:
        assert(ins < ArrLen(insCodes2));
        code = insCodes2[ins];
        break;
    case 2:
        assert(ins < ArrLen(insCodes3));
        code = insCodes3[ins];
        break;
    case 3:
        assert(ins < ArrLen(insCodes4));
        code = insCodes4[ins];
        break;
    case 4:
        assert(ins < ArrLen(insCodes5));
        code = insCodes5[ins];
        break;
    case 5:
        assert(ins < ArrLen(insCodes6));
        code = insCodes6[ins];
        break;
    case 6:
        assert(ins < ArrLen(insCodes7));
        code = insCodes7[ins];
        break;
    case 7:
        assert(ins < ArrLen(insCodes8));
        code = insCodes8[ins];
        break;
    case 8:
        assert(ins < ArrLen(insCodes9));
        code = insCodes9[ins];
        break;
    }
    
    assert((code != BAD_CODE));

    return  code;
}

// true if this 'imm' can be encoded as a input operand to a mov instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_mov(INT64 imm, emitAttr size)
{
    // Check for "MOV (wide immediate)".
    if (canEncodeHalfwordImm(imm, size))
        return true;

    // Next try the ones-complement form of 'halfword immediate' imm(i16,hw),
    // namely "MOV (inverted wide immediate)".
    ssize_t notOfImm = NOT_helper(imm, getBitWidth(size));
    if (canEncodeHalfwordImm(notOfImm, size))
        return true;

    // Finally try "MOV (bitmask immediate)" imm(N,r,s)
    if (canEncodeBitMaskImm(imm, size))
        return true;

    return false;
}

// true if this 'imm' can be encoded as a input operand to a vector movi instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_movi(INT64 imm, emitAttr elemsize)
{
    if (elemsize == EA_8BYTE)
    {
        UINT64 uimm = imm;
        while (uimm != 0)
        {
            INT64 loByte = uimm & 0xFF;
            if ((loByte == 0) || (loByte == 0xFF))
            {
                uimm >>= 8;
            }
            else
            {
                return false;
            }
        }
        assert(uimm == 0);
        return true;
    }
    else
    {
        // First try the standard 'byteShifted immediate' imm(i8,bySh)
        if (canEncodeByteShiftedImm(imm, elemsize, true))
            return true;
        
        // Next try the ones-complement form of the 'immediate' imm(i8,bySh)
        ssize_t notOfImm = NOT_helper(imm, getBitWidth(elemsize));
        if (canEncodeByteShiftedImm(notOfImm, elemsize, true))
            return true;
    }
    return false;
}

// true if this 'imm' can be encoded as a input operand to a fmov instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_fmov(double immDbl)
{
    if (canEncodeFloatImm8(immDbl))
        return true;
    
    return false;
}

// true if this 'imm' can be encoded as a input operand to an add instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_add(INT64 imm, emitAttr size)
{
    if (unsigned_abs(imm) <= 0x0fff)
        return true;
    else if (canEncodeWithShiftImmBy12(imm))     // Try the shifted by 12 encoding
        return true;
    
    return false;
}

// true if this 'imm' can be encoded as a input operand to an non-add/sub alu instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_cmp(INT64 imm, emitAttr size)
{
    return emitIns_valid_imm_for_add(imm, size);
}

// true if this 'imm' can be encoded as a input operand to an non-add/sub alu instruction 
/*static*/ bool emitter::emitIns_valid_imm_for_alu(INT64 imm, emitAttr size)
{
    if (canEncodeBitMaskImm(imm, size))
        return true;

    return false;
}

/************************************************************************
 *
 *   A helper method to return the natural scale for an EA 'size'
 */

/*static*/ unsigned emitter::NaturalScale_helper(emitAttr size)
{
    assert(size == EA_1BYTE || size == EA_2BYTE || size == EA_4BYTE || 
           size == EA_8BYTE || size == EA_16BYTE);
    
    unsigned result = 0;
    unsigned utemp  = (unsigned) size;
    
    // Compute log base 2 of utemp (aka 'size')
    while (utemp > 1)
    {
        result++;
        utemp >>= 1;
    }
    
    return result;
}

/************************************************************************
 *
 *  A helper method to perform a Rotate-Right shift operation 
 *  the source is 'value' and it is rotated right by 'sh' bits
 *  'value' is considered to be a fixed size 'width' set of bits.
 *
 *  Example
 *      value is '00001111', sh is 2 and width is 8
 *     result is '11000011'
 */

/*static*/ UINT64  emitter::ROR_helper(UINT64 value, unsigned sh, unsigned width)
{
    assert(width <= 64);
    // Check that 'value' fits in 'width' bits
    assert((width == 64) || (value < (1ULL << width)));
    // We don't support shifts >= width 
    assert(sh < width);

    UINT64 result;

    unsigned rsh = sh;
    unsigned lsh = width-rsh;

    result  = (value >> rsh);
    result |= (value << lsh);

    if (width < 64)
    {
        // mask off any extra bits that we got from the left shift
        result &= ((1ULL << width) - 1);  
    }
    return result;
}
/************************************************************************
 *
 *  A helper method to perform a 'NOT' bitwise complement operation.
 *  'value' is considered to be a fixed size 'width' set of bits.
 *
 *  Example
 *      value is '01001011', and width is 8
 *     result is '10110100'
 */

/*static*/ UINT64  emitter::NOT_helper(UINT64 value, unsigned width)
{
    assert(width <= 64);

    UINT64 result = ~value;

    if (width < 64)
    {
        // Check that 'value' fits in 'width' bits. Don't consider "sign" bits above width.
        UINT64 maxVal = 1ULL << width;
        UINT64 lowBitsMask = maxVal - 1;
        UINT64 signBitsMask = ~lowBitsMask | (1ULL << (width - 1)); // The high bits must be set, and the top bit (sign bit) must be set.
        assert((value < maxVal) ||
               ((value & signBitsMask) == signBitsMask));

        // mask off any extra bits that we got from the complement operation
        result &= lowBitsMask;
    }

    return result;
}


/************************************************************************
 *
 *  A helper method to perform a bit Replicate operation
 *  the source is 'value' with a fixed size 'width' set of bits.
 *  value is replicated to fill out 32 or 64 bits as determined by 'size'.
 *
 *  Example
 *      value is '11000011' (0xE3), width is 8 and size is EA_8BYTE
 *     result is '11000011 11000011 11000011 11000011 11000011 11000011 11000011 11000011'
 *               0xE3E3E3E3E3E3E3E3
 */

/*static*/ UINT64  emitter::Replicate_helper(UINT64 value, unsigned width, emitAttr size)
{
    assert(emitter::isValidGeneralDatasize(size));

    unsigned immWidth = (size == EA_8BYTE) ? 64 : 32;
    assert(width <= immWidth);

    UINT64 result = value;
    unsigned filledBits = width;

    while (filledBits < immWidth)
    {
        value <<= width;
        result |= value;
        filledBits += width;
    }
    return result;
}

/************************************************************************
 *
 *  Convert an imm(N,r,s) into a 64-bit immediate
 *  inputs 'bmImm' a bitMaskImm struct
 *         'size' specifies the size of the result (64 or 32 bits)
 */

/*static*/ INT64  emitter::emitDecodeBitMaskImm(const emitter::bitMaskImm bmImm, emitAttr  size)
{
    assert(isValidGeneralDatasize(size));   // Only EA_4BYTE or EA_8BYTE forms

    unsigned N = bmImm.immN;  // read the N,R and S values from the 'bitMaskImm' encoding 
    unsigned R = bmImm.immR;
    unsigned S = bmImm.immS;

    unsigned elemWidth = 64;   // used when immN == 1

    if (bmImm.immN == 0)       // find the smaller elemWidth when immN == 0
    {
        // Scan S for the highest bit not set
        elemWidth = 32;
        for (unsigned bitNum=5; bitNum > 0; bitNum--)
        {
            unsigned oneBit = elemWidth;
            if ((S & oneBit) == 0)
                break;
            elemWidth /= 2;
        }
    }
    else
    {
        assert(size == EA_8BYTE);
    }

    unsigned maskSR = elemWidth-1;

    S &= maskSR;
    R &= maskSR;

    // encoding for S is one less than the number of consecutive one bits 
    S++;  // Number of consecutive ones to generate in 'welem'

    // At this point:
    //
    //    'elemWidth' is the number of bits that we will use for the ROR and Replicate operations
    //    'S'         is the number of consecutive 1 bits for the immediate
    //    'R'         is the number of bits that we will Rotate Right the immediate
    //    'size'      selects the final size of the immedate that we return (64 or 32 bits)

    assert(S < elemWidth);   // 'elemWidth' consecutive one's is a reserved encoding 

    UINT64   welem;
    UINT64   wmask;
    
    welem = (1ULL << S) - 1;

    wmask = ROR_helper(welem, R, elemWidth);
    wmask = Replicate_helper(wmask, elemWidth, size);

    return wmask;
}


/*****************************************************************************
 *
 *  Check if an immediate can use the left shifted by 12 bits encoding
 */

/*static*/ bool emitter::canEncodeWithShiftImmBy12(INT64 imm)
{
    if (imm < 0)
    {
        imm = -imm;               // convert to unsigned
    }

    if ((imm & 0xfff) != 0)       // Now the low 12 bits all have to be zero
    {
        return false;
    }
    
    imm >>= 12;                   // shift right by 12 bits

    return (imm <= 0x0fff);       // Does it fit in 12 bits
}


/*****************************************************************************
 *
 *  Normalize the 'imm' so that the upper bits, as defined by 'size' are zero
 */

/*static*/ INT64  emitter::normalizeImm64(INT64 imm, emitAttr size)
{
    unsigned immWidth = getBitWidth(size);
    INT64    result   = imm;

    if (immWidth < 64)
    {
        // Check that 'imm' fits in 'immWidth' bits. Don't consider "sign" bits above width.
        INT64 maxVal       = 1LL << immWidth;
        INT64 lowBitsMask  = maxVal - 1;
        INT64 hiBitsMask   = ~lowBitsMask;
        INT64 signBitsMask = hiBitsMask | (1LL << (immWidth - 1)); // The high bits must be set, and the top bit (sign bit) must be set.
        assert((imm < maxVal) ||
               ((imm & signBitsMask) == signBitsMask));

        // mask off the hiBits 
        result &= lowBitsMask;
    }
    return result;
}


/*****************************************************************************
 *
 *  Normalize the 'imm' so that the upper bits, as defined by 'size' are zero
 */

/*static*/ INT32  emitter::normalizeImm32(INT32 imm, emitAttr size)
{
    unsigned immWidth = getBitWidth(size);
    INT32    result   = imm;

    if (immWidth < 32)
    {
        // Check that 'imm' fits in 'immWidth' bits. Don't consider "sign" bits above width.
        INT32 maxVal       = 1 << immWidth;
        INT32 lowBitsMask  = maxVal - 1;
        INT32 hiBitsMask   = ~lowBitsMask;
        INT32 signBitsMask = hiBitsMask | (1 << (immWidth - 1)); // The high bits must be set, and the top bit (sign bit) must be set.
        assert((imm < maxVal) ||
               ((imm & signBitsMask) == signBitsMask));

        // mask off the hiBits 
        result &= lowBitsMask;
    }
    return result;
}


/************************************************************************
 *
 *  returns true if 'imm' of 'size bits (32/64) can be encoded 
 *  using the ARM64 'bitmask immediate' form.
 *  When a non-null value is passed for 'wbBMI' then this method
 *  writes back the 'N','S' and 'R' values use to encode this immediate
 *
 */

/*static*/ bool  emitter::canEncodeBitMaskImm(INT64  imm,  emitAttr  size,
                                              emitter::bitMaskImm*  wbBMI)
{
    assert(isValidGeneralDatasize(size));   // Only EA_4BYTE or EA_8BYTE forms

    unsigned immWidth = (size == EA_8BYTE) ? 64 : 32;
    unsigned maxLen   = (size == EA_8BYTE) ? 6 : 5;

    imm = normalizeImm64(imm, size);

    // Starting with len=1, elemWidth is 2 bits
    //               len=2, elemWidth is 4 bits
    //               len=3, elemWidth is 8 bits
    //               len=4, elemWidth is 16 bits
    //               len=5, elemWidth is 32 bits
    // (optionally)  len=6, elemWidth is 64 bits
    //
    for (unsigned len=1; (len <= maxLen); len++)
    {
        unsigned elemWidth = 1 << len;
        UINT64   elemMask  = ((UINT64) -1) >> (64 - elemWidth);
        UINT64   tempImm   = (UINT64) imm;        // A working copy of 'imm' that we can mutate
        UINT64   elemVal   = tempImm & elemMask;  // The low 'elemWidth' bits of 'imm'
        
        // Check for all 1's or 0's as these can't be encoded
        if ((elemVal == 0) || (elemVal == elemMask))
            continue;

        // 'checkedBits' is the count of bits that are known to match 'elemVal' when replicated
        unsigned checkedBits = elemWidth;  // by definition the first 'elemWidth' bits match

        // Now check to see if each of the next bits match...
        //
        while (checkedBits < immWidth)
        {
            tempImm >>= elemWidth;

            UINT64 nextElem = tempImm & elemMask;
            if (nextElem != elemVal)
            {
                // Not matching, exit this loop and checkedBits will not be equal to immWidth
                break; 
            }

            // The 'nextElem' is matching, so increment 'checkedBits'
            checkedBits += elemWidth;
        }

        // Did the full immediate contain bits that can be formed by repeating 'elemVal'?
        if (checkedBits == immWidth)
        {
            // We are not quite done, since the only values that we can encode as a
            // 'bitmask immediate' are those that can be formed by starting with a 
            // bit string of 0*1* that is rotated by some number of bits.
            // 
            // We check to see if 'elemVal' can be formed using these restrictions.
            //
            // Observation: 
            // Rotating by one bit any value that passes these restrictions 
            // can be xor-ed with the original value and will result it a string
            // of bits that have exactly two 1 bits: 'elemRorXor'
            // Further the distance between the two one bits tells us the value
            // of S and the location of the 1 bits tells us the value of R
            //
            // Some examples:   (immWidth is 8)
            //
            // S=4,R=0   S=5,R=3   S=3,R=6
            // elemVal:        00001111  11100011  00011100
            // elemRor:        10000111  11110001  00001110
            // elemRorXor:     10001000  00010010  00010010
            //      compute S  45678---  ---5678-  ---3210-
            //      compute R  01234567  ---34567  ------67

            UINT64 elemRor    = ROR_helper(elemVal, 1, elemWidth);    // Rotate 'elemVal' Right by one bit
            UINT64 elemRorXor = elemVal ^ elemRor;             // Xor elemVal and elemRor

            // If we only have a two-bit change in elemROR then we can form a mask for this value
            unsigned bitCount = 0;
            UINT64   oneBit   = 0x1;
            unsigned R        = elemWidth;      // R is shift count for ROR (rotate right shift)
            unsigned S        = 0;              // S is number of consecutive one bits 
            int      incr     = -1;

            // Loop over the 'elemWidth' bits in 'elemRorXor'
            //
            for (unsigned bitNum=0; bitNum<elemWidth; bitNum++)
            {
                if (incr == -1)
                {
                    R--;         // We decrement R by one whenever incr is -1
                }
                if (bitCount == 1)
                {
                    S += incr;   // We incr/decr S, after we find the first one bit in 'elemRorXor'
                }

                // Is this bit position a 1 bit in 'elemRorXor'?
                //
                if (oneBit & elemRorXor)
                {
                    bitCount++;
                    // Is this the first 1 bit that we found in 'elemRorXor'?
                    if (bitCount == 1)
                    {
                        // Does this 1 bit represent a transition to zero bits?
                        bool toZeros = ((oneBit & elemVal) != 0);
                        if (toZeros)
                        {
                            // S :: Count down from elemWidth
                            S = elemWidth;
                            incr = -1;
                        }
                        else  // this 1 bit represent a transition to one bits.
                        {
                            // S :: Count up from zero
                            S = 0;
                            incr = +1;
                        }
                    }
                    else // bitCount > 1
                    {
                        // We found the second (or third...) 1 bit in 'elemRorXor'
                        incr = 0;  // stop decrementing 'R' 

                        if (bitCount > 2)
                        {
                            // More than 2 transitions from 0/1 in 'elemVal'
                            // This means that 'elemVal' can't be encoded
                            // using a 'bitmask immediate'.
                            //
                            // Furthermore, it will continue to fail 
                            // with any larger 'len' that we try.
                            // so just return false.
                            //
                            return false;
                        }
                    }
                }

                // shift oneBit left by one bit to test the next position
                oneBit <<= 1;
            }

            // We expect that bitCount will always be two at this point
            // but just in case return false for any bad cases.
            //
            assert(bitCount == 2);
            if (bitCount != 2)
                return false;
            
            // Perform some sanity checks on the values of 'S' and 'R'
            assert(S > 0);
            assert(S < elemWidth);
            assert(R < elemWidth);

            // Does the caller want us to return the N,R,S encoding values?
            //
            if (wbBMI != nullptr)
            {

                // The encoding used for S is one less than the 
                //  number of consecutive one bits
                S--;

                if (len == 6)
                {
                    wbBMI->immN = 1;
                }
                else
                {
                    wbBMI->immN = 0;
                    // The encoding used for 'S' here is a bit peculiar.
                    //
                    // The upper bits need to be complemented, followed by a zero bit
                    // then the value of 'S-1'
                    // 
                    unsigned upperBitsOfS = 64 - (1 << (len+1));
                    S |= upperBitsOfS;
                }
                wbBMI->immR = R;
                wbBMI->immS = S;

                // Verify that what we are returning is correct.
                assert(imm == emitDecodeBitMaskImm(*wbBMI, size));
            }
            // Tell the caller that we can successfully encode this immediate
            // using a 'bitmask immediate'.
            //
            return true;
        }
    }
    return false;
}

/************************************************************************
 *
 *  Convert a 64-bit immediate into its 'bitmask immediate' representation imm(N,r,s)
 */

/*static*/ emitter::bitMaskImm emitter::emitEncodeBitMaskImm(INT64 imm, emitAttr size)
{
    emitter::bitMaskImm result;
    result.immNRS = 0;

    bool canEncode = canEncodeBitMaskImm(imm, size, &result);
    assert(canEncode);

    return result;
}

/************************************************************************
 *
 *  Convert an imm(i16,hw) into a 32/64-bit immediate
 *  inputs 'hwImm' a halfwordImm struct
 *         'size' specifies the size of the result (64 or 32 bits)
 */

/*static*/ INT64  emitter::emitDecodeHalfwordImm(const emitter::halfwordImm hwImm, emitAttr size)
{
    assert(isValidGeneralDatasize(size));   // Only EA_4BYTE or EA_8BYTE forms

    unsigned  hw  =         hwImm.immHW;
    INT64     val = (INT64) hwImm.immVal;

    assert((hw <= 1) || (size == EA_8BYTE));
        
    INT64  result = val << (16 * hw);
    return result;
}

/************************************************************************
 *
 *  returns true if 'imm' of 'size' bits (32/64) can be encoded 
 *  using the ARM64 'halfword immediate' form.
 *  When a non-null value is passed for 'wbHWI' then this method
 *  writes back the 'immHW' and 'immVal' values use to encode this immediate
 *
 */

/*static*/ bool  emitter::canEncodeHalfwordImm(INT64  imm,  emitAttr  size,
                                               emitter::halfwordImm*  wbHWI)
{
    assert(isValidGeneralDatasize(size));   // Only EA_4BYTE or EA_8BYTE forms

    unsigned      immWidth = (size == EA_8BYTE) ? 64 : 32;
    unsigned      maxHW    = (size == EA_8BYTE) ? 4 : 2;

    // setup immMask to a (EA_4BYTE) 0x00000000_FFFFFFFF or (EA_8BYTE) 0xFFFFFFFF_FFFFFFFF
    const UINT64  immMask  = ((UINT64) -1) >> (64 - immWidth);
    const INT64   mask16   = (INT64) 0xFFFF;

    imm = normalizeImm64(imm, size);

    // Try each of the valid hw shift sizes
    for (unsigned hw=0; (hw < maxHW); hw++)
    {
        INT64 curMask   = mask16 << (hw * 16);   // Represents the mask of the bits in the current halfword
        INT64 checkBits = immMask & ~curMask;
 
        // Excluding the current halfword (using ~curMask)
        //  does the immediate have zero bits in every other bit that we care about?
        //  note we care about all 64-bits for EA_8BYTE 
        //  and we care about the lowest 32 bits for EA_4BYTE
        //
        if ((imm & checkBits) == 0)
        {
            // Does the caller want us to return the imm(i16,hw) encoding values?
            //
            if (wbHWI != nullptr)
            {
                INT64 val = ((imm & curMask) >> (hw * 16)) & mask16;
                wbHWI->immHW  = hw;
                wbHWI->immVal = val;

                // Verify that what we are returning is correct.
                assert(imm == emitDecodeHalfwordImm(*wbHWI, size));
            }
            // Tell the caller that we can successfully encode this immediate
            // using a 'halfword immediate'.
            //
            return true;
        }
    }
    return false;
}

/************************************************************************
 *
 *  Convert a 64-bit immediate into its 'halfword immediate' representation imm(i16,hw)
 */

/*static*/ emitter::halfwordImm emitter::emitEncodeHalfwordImm(INT64  imm,  emitAttr  size)
{
    emitter::halfwordImm result;
    result.immHWVal = 0;

    bool canEncode = canEncodeHalfwordImm(imm, size, &result);
    assert(canEncode);

    return result;
}

/************************************************************************
 *
 *  Convert an imm(i8,sh) into a 16/32-bit immediate
 *  inputs 'bsImm' a byteShiftedImm struct
 *         'size' specifies the size of the result (16 or 32 bits)
 */

/*static*/ INT32  emitter::emitDecodeByteShiftedImm(const emitter::byteShiftedImm bsImm, emitAttr size)
{
    bool      onesShift = (bsImm.immOnes == 1);
    unsigned  bySh      =         bsImm.immBY;          // Num Bytes to shift 0,1,2,3
    INT32     val       = (INT32) bsImm.immVal;         // 8-bit immediate
    INT32     result    = val;

    if (bySh > 0)
    {
        assert((size == EA_2BYTE) || (size == EA_4BYTE));   // Only EA_2BYTE or EA_4BYTE forms
        if (size == EA_2BYTE)
        {
            assert(bySh < 2);
        }
        else
        {
            assert(bySh < 4);
        }
        
        result <<= (8 * bySh);

        if (onesShift)
        {
            result |= ((1 << (8 * bySh)) - 1);
        }
    }
    return result;
}

/************************************************************************
 *
 *  returns true if 'imm' of 'size' bits (16/32) can be encoded 
 *  using the ARM64 'byteShifted immediate' form.
 *  When a non-null value is passed for 'wbBSI' then this method
 *  writes back the 'immBY' and 'immVal' values use to encode this immediate
 *
 */

/*static*/ bool  emitter::canEncodeByteShiftedImm(INT64 imm, emitAttr size, bool allow_MSL,
                                                  emitter::byteShiftedImm* wbBSI)
{
    bool     canEncode = false;
    bool     onesShift = false;   // true if we use the shifting ones variant
    unsigned bySh      = 0;       // number of bytes to shift: 0, 1, 2, 3
    unsigned imm8      = 0;       // immediate to use in the encoding

    imm = normalizeImm64(imm, size);

    if (size == EA_1BYTE)
    {
        imm8 = (unsigned) imm;
        assert(imm8 < 0x100);
        canEncode = true;
    }
    else if (size == EA_8BYTE)
    {
        imm8 = (unsigned) imm;
        assert(imm8 < 0x100);
        canEncode = true;
    }
    else
    {
        assert((size == EA_2BYTE) || (size == EA_4BYTE));   // Only EA_2BYTE or EA_4BYTE forms

        unsigned      immWidth = (size == EA_4BYTE) ? 32 : 16;
        unsigned      maxBY    = (size == EA_4BYTE) ? 4  : 2;

        // setup immMask to a (EA_2BYTE) 0x0000FFFF or (EA_4BYTE) 0xFFFFFFFF
        const UINT32  immMask  = ((UINT32) -1) >> (32 - immWidth);
        const INT32   mask8    = (INT32) 0xFF;

        // Try each of the valid by shift sizes
        for (bySh=0; (bySh < maxBY); bySh++)
        {
            INT32 curMask   = mask8 << (bySh * 8);   // Represents the mask of the bits in the current byteShifted
            INT32 checkBits = immMask & ~curMask;
            INT32 immCheck  = (imm & checkBits);
            
            // Excluding the current byte (using ~curMask)
            //  does the immediate have zero bits in every other bit that we care about?
            //  or can be use the shifted one variant?
            //  note we care about all 32-bits for EA_4BYTE 
            //  and we care about the lowest 16 bits for EA_2BYTE
            //
            if (immCheck == 0)
            {
                canEncode = true;
            }
            if (allow_MSL)
            {
                if ((bySh == 1) && (immCheck == 0xFF))
                {
                    canEncode = true;
                    onesShift = true;
                }
                else if ((bySh == 2) && (immCheck == 0xFFFF))
                {
                    canEncode = true;
                    onesShift = true;
                }
            }
            if (canEncode)
            {
                imm8 = (unsigned) (((imm & curMask) >> (bySh * 8)) & mask8);
                break;
            }
        }
    }
        
    if (canEncode)
    {
        // Does the caller want us to return the imm(i8,bySh) encoding values?
        //
        if (wbBSI != nullptr)
        {
            wbBSI->immOnes = onesShift; 
            wbBSI->immBY   = bySh;
            wbBSI->immVal  = imm8;
            
            // Verify that what we are returning is correct.
            assert(imm == emitDecodeByteShiftedImm(*wbBSI, size));
        }
        // Tell the caller that we can successfully encode this immediate
        // using a 'byteShifted immediate'.
        //
        return true;
    }
    return false;
}

/************************************************************************
 *
 *  Convert a 32-bit immediate into its 'byteShifted immediate' representation imm(i8,by)
 */

/*static*/ emitter::byteShiftedImm emitter::emitEncodeByteShiftedImm(INT64 imm, emitAttr size, bool allow_MSL)
{
    emitter::byteShiftedImm result;
    result.immBSVal = 0;

    bool canEncode = canEncodeByteShiftedImm(imm, size, allow_MSL, &result);
    assert(canEncode);

    return result;
}

/************************************************************************
 *
 *  Convert a 'float 8-bit immediate' into a double.
 *  inputs 'fpImm' a floatImm8 struct
 */

/*static*/ double  emitter::emitDecodeFloatImm8(const emitter::floatImm8 fpImm)
{
    unsigned sign  = fpImm.immSign;
    unsigned exp   = fpImm.immExp ^ 0x4;
    unsigned mant  = fpImm.immMant + 16;
    unsigned scale = 16 * 8;

    while (exp > 0)
    {
        scale /= 2;
        exp--;
    }

    double result = ((double) mant) / ((double) scale);
    if (sign == 1)
    {
        result = -result;
    }

    return result;
}

/************************************************************************
 *
 *  returns true if the 'immDbl' can be encoded using the 'float 8-bit immediate' form.
 *  also returns the encoding if wbFPI is non-null
 *
 */

/*static*/ bool  emitter::canEncodeFloatImm8(double  immDbl,  
                                             emitter::floatImm8*  wbFPI)
{
    bool    canEncode = false;
    double  val = immDbl;

    int sign = 0;
    if (val < 0.0)
    {
        val = -val;
        sign = 1;
    }

    int exp = 0;
    while ((val < 1.0) && (exp >= -4))
    {
        val *= 2.0;   
        exp--;
    }
    while ((val >= 2.0) && (exp <= 5))
    {
        val *= 0.5;
        exp++;
    }
    exp += 3;
    val *= 16.0;
    int ival = (int) val;
    
    if ((exp >= 0) && (exp <= 7))
    {
        if (val == (double) ival)
        {
            canEncode = true;
            
            if (wbFPI != nullptr)
            {
                ival -= 16;
                assert((ival >= 0) && (ival <= 15));

                wbFPI->immSign = sign;
                wbFPI->immExp  = exp ^ 0x4;
                wbFPI->immMant = ival;
                unsigned imm8 = wbFPI->immFPIVal;
                assert((imm8 >= 0) && (imm8 <= 0xff));
            }
        }
    }
    
    return canEncode;
}

/************************************************************************
 *
 *  Convert a double into its 'float 8-bit immediate' representation
 */

/*static*/ emitter::floatImm8 emitter::emitEncodeFloatImm8(double  immDbl)
{
    emitter::floatImm8 result;
    result.immFPIVal = 0;

    bool canEncode = canEncodeFloatImm8(immDbl, &result);
    assert(canEncode);

    return result;
}

/*****************************************************************************
 *
 *  For the given 'ins' returns the reverse instruction 
 *  if one exists, otherwise returns INS_INVALID
 */

/*static*/ instruction  emitter::insReverse(instruction ins)
{
    switch (ins)
    {
    case INS_add:
        return INS_sub;
    case INS_adds:
        return INS_subs;

    case INS_sub:
        return INS_add;
    case INS_subs:
        return INS_adds;

    case INS_cmp:
        return INS_cmn;
    case INS_cmn:
        return INS_cmp;
        
    case INS_ccmp:
        return INS_ccmn;
    case INS_ccmn:
        return INS_ccmp;
        
    default:
        return INS_invalid;
    }
}

/*****************************************************************************
 *
 *  For the given 'datasize' and 'elemsize', make the proper arrangement option
 *  returns the insOpts that specifies the vector register arrangement
 *  if one does not exist returns INS_OPTS_NONE
 */

/*static*/ insOpts  emitter::optMakeArrangement(emitAttr datasize, emitAttr elemsize)
{
    insOpts result = INS_OPTS_NONE;

    if (datasize == EA_8BYTE)
    {
        switch (elemsize)
        {
        case EA_1BYTE:
            result = INS_OPTS_8B;
            break;
        case EA_2BYTE:
            result = INS_OPTS_4H;
            break;
        case EA_4BYTE:
            result = INS_OPTS_2S;
            break;
        case EA_8BYTE:
            result = INS_OPTS_1D;
            break;
        }
    }
    else if (datasize == EA_16BYTE)
    {
        switch (elemsize)
        {
        case EA_1BYTE:
            result = INS_OPTS_16B;
            break;
        case EA_2BYTE:
            result = INS_OPTS_8H;
            break;
        case EA_4BYTE:
            result = INS_OPTS_4S;
            break;
        case EA_8BYTE:
            result = INS_OPTS_2D;
            break;
        }
    }
    return result;
}

/*****************************************************************************
 *
 *  For the given 'datasize' and arrangement 'opts' 
 *  returns true is the pair spcifies a valid arrangement
 */
/*static*/ bool  emitter::isValidArrangement(emitAttr datasize, insOpts opt)
{
    if (datasize == EA_8BYTE) 
    {
        if ((opt == INS_OPTS_8B) ||
            (opt == INS_OPTS_4H) ||
            (opt == INS_OPTS_2S) ||
            (opt == INS_OPTS_1D))
        {
            return true;
        }            
    }
    else if (datasize == EA_16BYTE) 
    {
        if ((opt == INS_OPTS_16B) ||
            (opt == INS_OPTS_8H)  ||
            (opt == INS_OPTS_4S)  ||
            (opt == INS_OPTS_2D))
        {
            return true;
        }                
    }
    return false;
}

//  For the given 'arrangement' returns the 'datasize' specified by the vector register arrangement
//  asserts and returns EA_UNKNOWN if an invalid 'arrangement' value is passed
//
/*static*/ emitAttr  emitter::optGetDatasize(insOpts arrangement)
{
    if ((arrangement == INS_OPTS_8B) ||
        (arrangement == INS_OPTS_4H) ||
        (arrangement == INS_OPTS_2S) ||
        (arrangement == INS_OPTS_1D))
    {
        return EA_8BYTE;
    }            
    else if ((arrangement == INS_OPTS_16B) ||
             (arrangement == INS_OPTS_8H)  ||
             (arrangement == INS_OPTS_4S)  ||
             (arrangement == INS_OPTS_2D))
    {
        return EA_16BYTE;
    }
    else
    {
        assert(!" invalid 'arrangement' value");
        return EA_UNKNOWN;
    }
}

//  For the given 'arrangement' returns the 'elemsize' specified by the vector register arrangement
//  asserts and returns EA_UNKNOWN if an invalid 'arrangement' value is passed
//
/*static*/ emitAttr  emitter::optGetElemsize(insOpts arrangement)
{
    if ((arrangement == INS_OPTS_8B) ||
        (arrangement == INS_OPTS_16B))
    {
        return EA_1BYTE;
    }
    else if ((arrangement == INS_OPTS_4H) ||
             (arrangement == INS_OPTS_8H))
    {
        return EA_2BYTE;
    }
    else if ((arrangement == INS_OPTS_2S) ||
             (arrangement == INS_OPTS_4S))
    {
        return EA_4BYTE;
    }
    else if ((arrangement == INS_OPTS_1D) ||
             (arrangement == INS_OPTS_2D))
    {
        return EA_8BYTE;
    }
    else
    {
        assert(!" invalid 'arrangement' value");
        return EA_UNKNOWN;
    }
}
 
//  For the given 'arrangement' returns the 'widen-arrangement' specified by the vector register arrangement
//  asserts and returns INS_OPTS_NONE if an invalid 'arrangement' value is passed
//
/*static*/ insOpts  emitter::optWidenElemsize(insOpts arrangement)
{
    if ((arrangement == INS_OPTS_8B) ||
        (arrangement == INS_OPTS_16B))
    {
        return INS_OPTS_8H;
    }
    else if ((arrangement == INS_OPTS_4H) ||
             (arrangement == INS_OPTS_8H))
    {
        return INS_OPTS_4S;
    }
    else if ((arrangement == INS_OPTS_2S) ||
             (arrangement == INS_OPTS_4S))
    {
        return INS_OPTS_2D;
    }
    else
    {
        assert(!" invalid 'arrangement' value");
        return INS_OPTS_NONE;
    }
}
 
//  For the given 'conversion' returns the 'dstsize' specified by the conversion option
/*static*/ emitAttr  emitter::optGetDstsize(insOpts conversion)
{
    switch (conversion)
    {
    case INS_OPTS_S_TO_8BYTE:
    case INS_OPTS_D_TO_8BYTE:
    case INS_OPTS_4BYTE_TO_D:
    case INS_OPTS_8BYTE_TO_D:
    case INS_OPTS_S_TO_D:
    case INS_OPTS_H_TO_D:

        return EA_8BYTE;

    case INS_OPTS_S_TO_4BYTE:
    case INS_OPTS_D_TO_4BYTE:
    case INS_OPTS_4BYTE_TO_S:
    case INS_OPTS_8BYTE_TO_S:
    case INS_OPTS_D_TO_S:
    case INS_OPTS_H_TO_S:

        return EA_4BYTE;

    case INS_OPTS_S_TO_H:
    case INS_OPTS_D_TO_H:

        return EA_2BYTE;

    default:
        assert(!" invalid 'conversion' value");
        return EA_UNKNOWN;
    }
}

//  For the given 'conversion' returns the 'srcsize' specified by the conversion option
/*static*/ emitAttr  emitter::optGetSrcsize(insOpts conversion)
{
    switch (conversion)
    {
    case INS_OPTS_D_TO_8BYTE:
    case INS_OPTS_D_TO_4BYTE:
    case INS_OPTS_8BYTE_TO_D:
    case INS_OPTS_8BYTE_TO_S:
    case INS_OPTS_D_TO_S:
    case INS_OPTS_D_TO_H:

        return EA_8BYTE;

    case INS_OPTS_S_TO_8BYTE:
    case INS_OPTS_S_TO_4BYTE:
    case INS_OPTS_4BYTE_TO_S:
    case INS_OPTS_4BYTE_TO_D:
    case INS_OPTS_S_TO_D:
    case INS_OPTS_S_TO_H:

        return EA_4BYTE;

    case INS_OPTS_H_TO_S:
    case INS_OPTS_H_TO_D:

        return EA_2BYTE;

    default:
        assert(!" invalid 'conversion' value");
        return EA_UNKNOWN;
    }
}

//    For the given 'size' and 'index' returns true if it specifies a valid index for a vector register of 'size'
/*static*/ bool  emitter::isValidVectorIndex(emitAttr datasize, emitAttr elemsize, ssize_t index)
{
    assert(isValidVectorDatasize(datasize));
    assert(isValidVectorElemsize(elemsize));
    
    bool result = false;
    if (index >= 0)
    {
        if (datasize == EA_8BYTE)
        {
            switch (elemsize)
            {
            case EA_1BYTE:
                result = (index < 8);
                break;
            case EA_2BYTE:
                result = (index < 4);
                break;
            case EA_4BYTE:
                result = (index < 2);
                break;
            case EA_8BYTE:
                result = (index < 1);
                break;
            }
        }
        else if (datasize == EA_16BYTE)
        {
            switch (elemsize)
            {
            case EA_1BYTE:
                result = (index < 16);
                break;
            case EA_2BYTE:
                result = (index < 8);
                break;
            case EA_4BYTE:
                result = (index < 4);
                break;
            case EA_8BYTE:
                result = (index < 2);
                break;
            }
        }
    }
    return result;
}

/*****************************************************************************
 *
 *  Add an instruction with no operands.
 */

void                emitter::emitIns(instruction ins)
{
    instrDesc *  id   = emitNewInstrSmall(EA_8BYTE);
    insFormat    fmt  = emitInsFormat(ins);

    assert(fmt == IF_SN_0A);

    id->idIns(ins);
    id->idInsFmt(fmt);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction with a single immediate value.
 */

void                emitter::emitIns_I(instruction ins,
                                       emitAttr    attr,
                                       ssize_t     imm)
{
    insFormat  fmt = IF_NONE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_brk:
        if ((imm & 0x0000ffff) == imm)
        {
            fmt = IF_SI_0A;
        }
        else
        {
            assert(!"Instruction cannot be encoded: IF_SI_0A");
        }
        break;
    }
    assert(fmt != IF_NONE);

    instrDesc *  id = emitNewInstrSC(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing a single register.
 */

void                emitter::emitIns_R(instruction ins,
                                       emitAttr    attr,
                                       regNumber   reg)
{
    emitAttr     size = EA_SIZE(attr);
    insFormat    fmt  = IF_NONE;
    instrDesc *  id   = nullptr;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_blr:
    case INS_br:
        assert(isGeneralRegister(reg));
        id = emitNewInstrCns(attr, 0);
        fmt = IF_BR_1A;
        break;

    case INS_ret:
        assert(isGeneralRegister(reg));
        id = emitNewInstrSmall(attr);
        fmt = IF_BR_1A;
        break;

    }
    assert(fmt != IF_NONE);

    if (id != nullptr)
    {
        id->idIns(ins);
        id->idInsFmt(fmt);
        id->idReg1(reg);

        dispIns(id);
        appendToCurIG(id);
    }
}

/*****************************************************************************
 *
 *  Add an instruction referencing a register and a constant.
 */

void                emitter::emitIns_R_I(instruction ins,
                                         emitAttr    attr,
                                         regNumber   reg,
                                         ssize_t     imm,
                                         insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size      = EA_SIZE(attr);
    emitAttr   elemsize  = EA_UNKNOWN;
    insFormat  fmt       = IF_NONE;
    bool       canEncode = false;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
        bitMaskImm      bmi;
        halfwordImm     hwi;
        byteShiftedImm  bsi;
        ssize_t         notOfImm;

    case INS_tst:
        assert(insOptsNone(opt));
        assert(isGeneralRegister(reg));
        bmi.immNRS = 0;
        canEncode = canEncodeBitMaskImm(imm, size, &bmi);
        if (canEncode)
        {
            imm = bmi.immNRS;
            assert(isValidImmNRS(imm, size));
            fmt = IF_DI_1C;
        }
        break;

    case INS_movk:
    case INS_movn:
    case INS_movz:
        assert(isValidGeneralDatasize(size));
        assert(insOptsNone(opt));    // No LSL here (you must use emitIns_R_I_I if a shift is needed)
        assert(isGeneralRegister(reg));
        assert(isValidUimm16(imm));

        hwi.immHW  = 0;
        hwi.immVal = imm;
        assert(imm == emitDecodeHalfwordImm(hwi, size));

        imm = hwi.immHWVal;
        canEncode = true;
        fmt = IF_DI_1B;
        break;

    case INS_mov:
        assert(isValidGeneralDatasize(size));
        assert(insOptsNone(opt));    // No explicit LSL here 
        // We will automatically determine the shift based upon the imm

        // First try the standard 'halfword immediate' imm(i16,hw)
        hwi.immHWVal = 0;
        canEncode = canEncodeHalfwordImm(imm, size, &hwi);
        if (canEncode)
        {
            // uses a movz encoding
            assert(isGeneralRegister(reg));
            imm = hwi.immHWVal;
            assert(isValidImmHWVal(imm, size));
            fmt = IF_DI_1B;
            break;
        }

        // Next try the ones-complement form of 'halfword immediate' imm(i16,hw)
        notOfImm = NOT_helper(imm, getBitWidth(size));
        canEncode = canEncodeHalfwordImm(notOfImm, size, &hwi);
        if (canEncode)
        {
            assert(isGeneralRegister(reg));
            imm = hwi.immHWVal;
            ins = INS_movn;      // uses a movn encoding
            assert(isValidImmHWVal(imm, size));
            fmt = IF_DI_1B;
            break;
        }

        // Finally try the 'bitmask immediate' imm(N,r,s)
        bmi.immNRS = 0;
        canEncode = canEncodeBitMaskImm(imm, size, &bmi);
        if (canEncode)
        {
            assert(isGeneralRegisterOrSP(reg));
            reg = encodingSPtoZR(reg);
            imm = bmi.immNRS;
            assert(isValidImmNRS(imm, size));
            fmt = IF_DI_1D;
            break;
        }
        else
        {
            assert(!"Instruction cannot be encoded: mov imm");
        }

        break;

    case INS_movi:
        assert(isValidVectorDatasize(size));
        assert(isVectorRegister(reg));
        if (insOptsNone(opt) && (size == EA_8BYTE))
        {
            opt = INS_OPTS_1D;
        }
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        
        if (elemsize == EA_8BYTE)
        {
            size_t  uimm = imm;
            ssize_t imm8 = 0;
            unsigned pos = 0;
            canEncode = true;
            bool failed = false;
            while (uimm != 0)
            {
                INT64 loByte = uimm & 0xFF;
                if (((loByte == 0) || (loByte == 0xFF)) && (pos < 8))
                {
                    if (loByte == 0xFF)
                    {
                        imm8 |= (1 << pos);
                    }
                    uimm >>= 8;
                    pos++;
                }
                else
                {
                    canEncode = false;
                    break;
                }
            }
            imm = imm8;
            assert(isValidUimm8(imm));
            fmt = IF_DV_1B;
            break;
        } 
        else
        {
            // Vector operation

            // No explicit LSL/MSL is used for the immediate 
            // We will automatically determine the shift based upon the value of imm
        
            // First try the standard 'byteShifted immediate' imm(i8,bySh)
            bsi.immBSVal = 0;
            canEncode = canEncodeByteShiftedImm(imm, elemsize, true, &bsi);
            if (canEncode)
            {
                imm = bsi.immBSVal;
                assert(isValidImmBSVal(imm, size));
                fmt = IF_DV_1B;
                break;
            }

            // Next try the ones-complement form of the 'immediate' imm(i8,bySh)
            if ((elemsize == EA_2BYTE) || (elemsize == EA_4BYTE))  // Only EA_2BYTE or EA_4BYTE forms
            {
                notOfImm = NOT_helper(imm, getBitWidth(elemsize));
                canEncode = canEncodeByteShiftedImm(notOfImm, elemsize, true, &bsi);
                if (canEncode)
                {
                    imm = bsi.immBSVal;
                    ins = INS_mvni;      // uses a mvni encoding
                    assert(isValidImmBSVal(imm, size));
                    fmt = IF_DV_1B;
                    break;
                }
            }
        }
        break;
        
    case INS_orr:
    case INS_bic:
    case INS_mvni:
        assert(isValidVectorDatasize(size));
        assert(isVectorRegister(reg));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert((elemsize == EA_2BYTE) || (elemsize == EA_4BYTE));  // Only EA_2BYTE or EA_4BYTE forms

        // Vector operation         

        // No explicit LSL/MSL is used for the immediate 
        // We will automatically determine the shift based upon the value of imm
        
        // First try the standard 'byteShifted immediate' imm(i8,bySh)
        bsi.immBSVal = 0;
        canEncode = canEncodeByteShiftedImm(imm, elemsize, 
                                            (ins == INS_mvni),  // mvni supports the ones shifting variant (aka MSL)
                                            &bsi);
        if (canEncode)
        {
            imm = bsi.immBSVal;
            assert(isValidImmBSVal(imm, size));
            fmt = IF_DV_1B;
            break;
        }
        break;

    case INS_cmp:
    case INS_cmn:
        assert(insOptsNone(opt));
        assert(isGeneralRegister(reg));

        if (unsigned_abs(imm) <= 0x0fff)
        {
            if (imm < 0)
            {
                ins = insReverse(ins);
                imm = -imm;
            }
            assert(isValidUimm12(imm));
            canEncode = true;
            fmt = IF_DI_1A;
        }
        else if (canEncodeWithShiftImmBy12(imm))     // Try the shifted by 12 encoding
        {
            // Encoding will use a 12-bit left shift of the immediate
            opt = INS_OPTS_LSL12;
            if (imm < 0)
            {
                ins = insReverse(ins);
                imm = -imm;
            }
            assert((imm & 0xfff) == 0);
            imm >>= 12;
            assert(isValidUimm12(imm));
            canEncode = true;
            fmt = IF_DI_1A;
        }
        else
        {
            assert(!"Instruction cannot be encoded: IF_DI_1A");
        }
        break;

    }  // end switch (ins)

    assert(canEncode);
    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing a register and a floating point constant.
 */

void                emitter::emitIns_R_F(instruction ins,
                                         emitAttr    attr,
                                         regNumber   reg,
                                         double      immDbl,
                                         insOpts     opt   /* = INS_OPTS_NONE */)

{
    emitAttr   size      = EA_SIZE(attr);
    emitAttr   elemsize  = EA_UNKNOWN;
    insFormat  fmt       = IF_NONE;
    ssize_t    imm       = 0;
    bool       canEncode = false;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
        floatImm8 fpi;

    case INS_fcmp:
    case INS_fcmpe:
        assert(insOptsNone(opt));
        assert(isValidVectorElemsizeFloat(size));
        assert(isVectorRegister(reg));
        if (immDbl == 0.0)
        {
            canEncode = true;
            fmt = IF_DV_1C;
        }
        break;

    case INS_fmov:
        assert(isVectorRegister(reg));
        fpi.immFPIVal = 0;
        canEncode = canEncodeFloatImm8(immDbl, &fpi);

        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(opt != INS_OPTS_1D);    // Reserved encoding

            if (canEncode)
            {
                imm = fpi.immFPIVal;
                assert((imm >= 0) && (imm <= 0xff));
                fmt = IF_DV_1B;
            }
        }
        else
        {
            // Scalar operation
            assert(insOptsNone(opt));
            assert(isValidVectorElemsizeFloat(size));

            if (canEncode)
            {
                imm = fpi.immFPIVal;
                assert((imm >= 0) && (imm <= 0xff));
                fmt = IF_DV_1A;
            }
        }
        break;
    }  // end switch (ins)

    assert(canEncode);
    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing two registers
 */

void                emitter::emitIns_R_R(instruction ins,
                                         emitAttr    attr,
                                         regNumber   reg1,
                                         regNumber   reg2,
                                         insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size     = EA_SIZE(attr);
    emitAttr   elemsize = EA_UNKNOWN;
    insFormat  fmt      = IF_NONE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_mov:
        assert(insOptsNone(opt));
        // Is the mov even necessary?
        if (reg1 == reg2)
        {
            // A mov with a EA_4BYTE has the side-effect of clearing the upper bits
            // So only eliminate mov instructions that are not clearing the upper bits
            //
            if (isGeneralRegisterOrSP(reg1) && (size == EA_8BYTE))
            {
                return;
            }
            else if (isVectorRegister(reg1) && (size == EA_16BYTE))
            {
                return;
            }
        }

        // Check for the 'mov' aliases for the vector registers
        if (isVectorRegister(reg1))
        {           
            if (isVectorRegister(reg2) && isValidVectorDatasize(size))
            {
                return emitIns_R_R_R(INS_mov, size, reg1, reg2, reg2);
            }
            else
            {
                return emitIns_R_R_I(INS_mov, size, reg1, reg2, 0);
            }
        }
        else
        {
            if (isVectorRegister(reg2))
            {
                assert(isGeneralRegister(reg1));
                return emitIns_R_R_I(INS_mov, size, reg1, reg2, 0);
            }
        }

        // Is this a MOV to/from SP instruction?
        if ((reg1 == REG_SP) || (reg2 == REG_SP))
        {
            assert(isGeneralRegisterOrSP(reg1));
            assert(isGeneralRegisterOrSP(reg2));
            reg1 = encodingSPtoZR(reg1);
            reg2 = encodingSPtoZR(reg2);
            fmt = IF_DR_2G;
        }
        else
        {
            assert(insOptsNone(opt));
            assert(isGeneralRegister(reg1));
            assert(isGeneralRegisterOrZR(reg2));
            fmt = IF_DR_2E;
        }
        break;
        
    case INS_abs:
    case INS_not:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        // for 'NOT' we can construct the arrangement: 8B or 16B
        if ((ins == INS_not) && insOptsNone(opt))
        {
            assert(isValidVectorDatasize(size));
            elemsize = EA_1BYTE;
            opt = optMakeArrangement(size, elemsize);
        }
        if (insOptsNone(opt))
        {
            // Scalar operation
            assert(size == EA_8BYTE);  // Only type D is supported
            fmt = IF_DV_2L;
        }
        else
        {
            // Vector operation
            assert(insOptsAnyArrangement(opt));
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            if (ins == INS_not)
            {
                assert(elemsize == EA_1BYTE);
            }
            fmt = IF_DV_2M;
        }
        break;

    case INS_mvn:
    case INS_neg:
        if (isVectorRegister(reg1))
        {
            assert(isVectorRegister(reg2));
            // for 'mvn' we can construct the arrangement: 8B or 16b
            if ((ins == INS_mvn) && insOptsNone(opt))
            {
                assert(isValidVectorDatasize(size));
                elemsize = EA_1BYTE;
                opt = optMakeArrangement(size, elemsize);
            }
            if (insOptsNone(opt))
            {
                // Scalar operation
                assert(size == EA_8BYTE);  // Only type D is supported
                fmt = IF_DV_2L;
            }
            else
            {
                // Vector operation
                assert(isValidVectorDatasize(size));
                assert(isValidArrangement(size, opt));
                elemsize = optGetElemsize(opt);
                if (ins == INS_mvn)
                {
                    assert(elemsize == EA_1BYTE);  // Only supports 8B or 16B
                }
                fmt = IF_DV_2M;
            }
            break;
        }
        __fallthrough;
        
    case INS_negs:
        assert(insOptsNone(opt));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegisterOrZR(reg2));
        fmt = IF_DR_2E;
        break;

    case INS_sxtw:
        assert(size == EA_8BYTE);
        __fallthrough;

    case INS_sxtb:
    case INS_sxth:
    case INS_uxtb:
    case INS_uxth:
        assert(insOptsNone(opt));
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        fmt = IF_DR_2H;
        break;

    case INS_sxtl:
    case INS_sxtl2:
    case INS_uxtl:
    case INS_uxtl2:
        return emitIns_R_R_I(ins, size, reg1, reg2, 0, opt);

    case INS_cls:
    case INS_clz:
    case INS_rbit:
    case INS_rev16:
    case INS_rev32:
    case INS_cnt:
        if (isVectorRegister(reg1))
        {
            assert(isVectorRegister(reg2));
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            if ((ins == INS_cls) || (ins == INS_clz))
            {
                assert(elemsize != EA_8BYTE);   // No encoding for type D
            }
            else if (ins == INS_rev32)
            {
                assert((elemsize == EA_2BYTE) || (elemsize == EA_1BYTE));
            }
            else
            {
                assert(elemsize == EA_1BYTE);  // Only supports 8B or 16B
            }
            fmt = IF_DV_2M;
            break;
        }
        if (ins == INS_cnt)
        {
            // Doesn't have general register version(s)
            break;
        }

        __fallthrough;
        
    case INS_rev:
        assert(insOptsNone(opt));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        if (ins == INS_rev32)
        {
            assert(size == EA_8BYTE);
        }
        else
        {
            assert(isValidGeneralDatasize(size));
        }
        fmt = IF_DR_2G;
        break;

    case INS_rev64:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isValidVectorDatasize(size));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(elemsize != EA_8BYTE);   // No encoding for type D
        fmt = IF_DV_2M;
        break;

    case INS_ldr:
    case INS_ldrb:
    case INS_ldrh:
    case INS_ldrsb:
    case INS_ldrsh:
    case INS_ldrsw:
    case INS_str:
    case INS_strb:
    case INS_strh:

    case INS_cmp:
    case INS_cmn:
    case INS_tst:
        assert(insOptsNone(opt));
        emitIns_R_R_I(ins, attr, reg1, reg2, 0, INS_OPTS_NONE);
        return;

    case INS_fmov:
        assert(isValidVectorElemsizeFloat(size));

        // Is the mov even necessary?
        if (reg1 == reg2)
        {
            return;
        }
        
        if (isVectorRegister(reg1))
        {           
            if (isVectorRegister(reg2))
            {
                assert(insOptsNone(opt));
                fmt = IF_DV_2G;
            }
            else
            {
                 assert(isGeneralRegister(reg2));

                // if the optional conversion specifier is not present we calculate it
                if (opt == INS_OPTS_NONE)
                {
                    opt = (size == EA_4BYTE) ? INS_OPTS_4BYTE_TO_S
                                             : INS_OPTS_8BYTE_TO_D;
                }
                assert(insOptsConvertIntToFloat(opt));
               
                fmt = IF_DV_2I;
            }
        }
        else
        {
            assert(isGeneralRegister(reg1));
            assert(isVectorRegister(reg2));

            // if the optional conversion specifier is not present we calculate it
            if (opt == INS_OPTS_NONE)
            {
                opt = (size == EA_4BYTE) ? INS_OPTS_S_TO_4BYTE
                                         : INS_OPTS_D_TO_8BYTE;
            }
            assert(insOptsConvertFloatToInt(opt));

            fmt = IF_DV_2H;
        }
        break;

    case INS_fcmp:
    case INS_fcmpe:
        assert(insOptsNone(opt));
        assert(isValidVectorElemsizeFloat(size));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        fmt = IF_DV_2K;
        break;

    case INS_fcvtns:
    case INS_fcvtnu:
    case INS_fcvtas:
    case INS_fcvtau:
    case INS_fcvtps:
    case INS_fcvtpu:
    case INS_fcvtms:
    case INS_fcvtmu:
    case INS_fcvtzs:
    case INS_fcvtzu:
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isVectorRegister(reg1));
            assert(isVectorRegister(reg2));
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_2A;
        }
        else
        {
            // Scalar operation
            assert(isVectorRegister(reg2));
            if (isVectorRegister(reg1))
            {
                assert(insOptsNone(opt));
                assert(isValidVectorElemsizeFloat(size));
                fmt = IF_DV_2G;
            }
            else
            {
                assert(isGeneralRegister(reg1));
                assert(insOptsConvertFloatToInt(opt));
                assert(isValidVectorElemsizeFloat(size));
                fmt = IF_DV_2H;
            }
        }
        break;

    case INS_scvtf:
    case INS_ucvtf:
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isVectorRegister(reg1));
            assert(isVectorRegister(reg2));
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_2A;
        }
        else
        {
            // Scalar operation
            assert(isVectorRegister(reg1));
            if (isVectorRegister(reg2))
            {
                assert(insOptsNone(opt));
                assert(isValidVectorElemsizeFloat(size));
                fmt = IF_DV_2G;
            }
            else
            {
                assert(isGeneralRegister(reg2));
                assert(insOptsConvertIntToFloat(opt));
                assert(isValidVectorElemsizeFloat(size));
                fmt = IF_DV_2I;
            }
        }
        break;

    case INS_fabs:
    case INS_fneg:
    case INS_fsqrt:
    case INS_frinta:
    case INS_frinti:
    case INS_frintm:
    case INS_frintn:
    case INS_frintp:
    case INS_frintx:
    case INS_frintz:
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isVectorRegister(reg1));
            assert(isVectorRegister(reg2));
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_2A;
        }
        else
        {
            // Scalar operation
            assert(insOptsNone(opt));
            assert(isValidVectorElemsizeFloat(size));
            assert(isVectorRegister(reg1));
            assert(isVectorRegister(reg2));
            fmt = IF_DV_2G;
        }
        break;

    case INS_fcvt:
        assert(insOptsConvertFloatToFloat(opt));
        assert(isValidVectorFcvtsize(size));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        fmt = IF_DV_2J;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSmall(attr);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing a register and two constants.
 */

void                emitter::emitIns_R_I_I(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg,
                                           ssize_t     imm1,
                                           ssize_t     imm2,
                                           insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size   = EA_SIZE(attr);
    insFormat  fmt    = IF_NONE;
    size_t     immOut = 0;   // composed from imm1 and imm2 and stored in the instrDesc

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
        bool            canEncode;
        halfwordImm     hwi;

    case INS_mov:
        ins = INS_movz;   // INS_mov with LSL is an alias for INS_movz LSL
        __fallthrough;

    case INS_movk:
    case INS_movn:
    case INS_movz:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg));
        assert(isValidUimm16(imm1));
        assert(insOptsLSL(opt));                    // Must be INS_OPTS_LSL

        if (size == EA_8BYTE)
        {
            assert((imm2 ==  0) || (imm2 == 16) ||      // shift amount: 0, 16, 32 or 48
                   (imm2 == 32) || (imm2 == 48));
        }
        else // EA_4BYTE
        {
            assert((imm2 == 0) || (imm2 == 16));        // shift amount: 0 or 16
        }

        hwi.immHWVal = 0;

        switch (imm2) {
        case 0:
            hwi.immHW  = 0;
            canEncode = true; 
           break;
            
        case 16:
            hwi.immHW  = 1;
            canEncode = true;
            break;
            
        case 32:
            hwi.immHW  = 2;
            canEncode = true;
            break;
            
        case 48:
            hwi.immHW  = 3;
            canEncode = true;
            break;

        default:
            canEncode = false;
        }

        if (canEncode)
        {
            hwi.immVal = imm1;

            immOut = hwi.immHWVal;
            assert(isValidImmHWVal(immOut, size));
            fmt = IF_DI_1B;
        }
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSC(attr, immOut);

    id->idIns(ins);
    id->idInsFmt(fmt);

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing two registers and a constant.
 */

void                emitter::emitIns_R_R_I(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg1,
                                           regNumber   reg2,
                                           ssize_t     imm,
                                           insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size     = EA_SIZE(attr);
    emitAttr   elemsize = EA_UNKNOWN;
    insFormat  fmt      = IF_NONE;
    bool       isLdSt   = false;
    bool       isSIMD   = false;
    bool       isAddSub = false;
    bool       setFlags = false;
    unsigned   scale    = 0;
    bool       unscaledOp = false;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
        bool            canEncode;
        bitMaskImm      bmi;

    case INS_mov:
        // Check for the 'mov' aliases for the vector registers
        assert(insOptsNone(opt));
        assert(isValidVectorElemsize(size));
        elemsize = size;
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));

        if (isVectorRegister(reg1))
        {
            if (isGeneralRegisterOrZR(reg2))
            {
                fmt = IF_DV_2C;   // Alias for 'ins'
                break;
            }
            else if (isVectorRegister(reg2))
            {
                fmt = IF_DV_2E;   // Alias for 'dup'
                break;
            }
        }
        else // isGeneralRegister(reg1)
        {
            assert(isGeneralRegister(reg1));
            if (isVectorRegister(reg2))
            {
                fmt = IF_DV_2B;   // Alias for 'umov'
                break;
            }
        }
        assert(!" invalid INS_mov operands");
        break;

    case INS_lsl:
    case INS_lsr:
    case INS_asr:
        assert(insOptsNone(opt));
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isValidImmShift(imm, size));
        fmt = IF_DI_2D;
        break;

    case INS_ror:
        assert(insOptsNone(opt));
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isValidImmShift(imm, size));
        fmt = IF_DI_2B;
        break;

    case INS_sshr:
    case INS_ssra:
    case INS_srshr:
    case INS_srsra:
    case INS_shl:
    case INS_ushr:
    case INS_usra:
    case INS_urshr:
    case INS_ursra:
    case INS_sri:
    case INS_sli:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsize(elemsize));
            assert(isValidImmShift(imm, elemsize));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_2O;
            break;
        }
        else
        {
            // Scalar operation
            assert(insOptsNone(opt));
            assert(size == EA_8BYTE);    // only supported size
            assert(isValidImmShift(imm, size));
            fmt = IF_DV_2N;
        }
        break;


    case INS_sxtl:
    case INS_uxtl:
        assert(imm == 0);
        __fallthrough;

    case INS_shrn:
    case INS_rshrn:
    case INS_sshll:
    case INS_ushll:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        // Vector operation
        assert(size == EA_8BYTE);
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(elemsize != EA_8BYTE);  // Reserved encodings
        assert(isValidVectorElemsize(elemsize));
        assert(isValidImmShift(imm, elemsize));
        fmt = IF_DV_2O;
        break;

    case INS_sxtl2:
    case INS_uxtl2:
        assert(imm == 0);
        __fallthrough;

    case INS_shrn2:
    case INS_rshrn2:
    case INS_sshll2:
    case INS_ushll2:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        // Vector operation
        assert(size == EA_16BYTE);
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(elemsize != EA_8BYTE);  // Reserved encodings
        assert(isValidVectorElemsize(elemsize));
        assert(isValidImmShift(imm, elemsize));
        fmt = IF_DV_2O;
        break;

    case INS_mvn:
    case INS_neg:
    case INS_negs:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegisterOrZR(reg2));

        if (imm == 0)
        {
            assert(insOptsNone(opt));       // a zero imm, means no alu shift kind

            fmt = IF_DR_2E;
        }
        else
        {
            if (ins == INS_mvn)
            {
                assert(insOptsAnyShift(opt));   // a non-zero imm, must select shift kind
            }
            else // neg or negs
            {
                assert(insOptsAluShift(opt));   // a non-zero imm, must select shift kind, can't use ROR
            }
            assert(isValidImmShift(imm, size));
            fmt = IF_DR_2F;
        }
        break;

    case INS_tst:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegisterOrZR(reg1));
        assert(isGeneralRegister(reg2));

        if (insOptsAnyShift(opt))
        {
            assert(isValidImmShift(imm, size) && (imm != 0));
            fmt = IF_DR_2B;
        }
        else
        {
            assert(insOptsNone(opt));       // a zero imm, means no alu shift kind
            assert(imm == 0);
            fmt = IF_DR_2A;
        }
        break;

    case INS_cmp:
    case INS_cmn:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegisterOrSP(reg1));
        assert(isGeneralRegister(reg2));
        
        reg1 = encodingSPtoZR(reg1);
        if (insOptsAnyExtend(opt))
        {
            assert((imm >= 0) && (imm <= 4));

            fmt = IF_DR_2C;
        }
        else if (imm == 0)
        {
            assert(insOptsNone(opt));       // a zero imm, means no alu shift kind

            fmt = IF_DR_2A;
        }
        else
        {
            assert(insOptsAnyShift(opt));   // a non-zero imm, must select shift kind
            assert(isValidImmShift(imm, size));
            fmt = IF_DR_2B;
        }
        break;

    case INS_ands:
    case INS_and:
    case INS_eor:
    case INS_orr:
        assert(insOptsNone(opt));
        assert(isGeneralRegister(reg2));
        if (ins == INS_ands)
        {
            assert(isGeneralRegister(reg1));
        }
        else
        {
            assert(isGeneralRegisterOrSP(reg1));
            reg1 = encodingSPtoZR(reg1);
        }

        bmi.immNRS = 0;
        canEncode = canEncodeBitMaskImm(imm, size, &bmi);
        if (canEncode)
        {
            imm = bmi.immNRS;
            assert(isValidImmNRS(imm, size));
            fmt = IF_DI_2C;
        }
        break;

    case INS_dup:  // by element, imm selects the element of reg2
        assert(isVectorRegister(reg1));
        if (isVectorRegister(reg2))
        {
            if (insOptsAnyArrangement(opt))
            {
                // Vector operation
                assert(isValidVectorDatasize(size));
                assert(isValidArrangement(size, opt));
                elemsize = optGetElemsize(opt);
                assert(isValidVectorElemsize(elemsize));
                assert(isValidVectorIndex(size, elemsize, imm));
                assert(opt != INS_OPTS_1D);  // Reserved encoding
                fmt = IF_DV_2D;
                break;
            }
            else
            {
                // Scalar operation
                assert(insOptsNone(opt));
                elemsize = size;
                assert(isValidVectorElemsize(elemsize));
                assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
                fmt = IF_DV_2E;
                break;
            }
        }
        __fallthrough;

    case INS_ins: // (MOV from general)
        assert(insOptsNone(opt));
        assert(isValidVectorElemsize(size));
        assert(isVectorRegister(reg1));
        assert(isGeneralRegisterOrZR(reg2));
        elemsize = size;
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        fmt = IF_DV_2C;
        break;

    case INS_umov:  // (MOV to general)
        assert(insOptsNone(opt));
        assert(isValidVectorElemsize(size));
        assert(isGeneralRegister(reg1));
        assert(isVectorRegister(reg2));
        elemsize = size;
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        fmt = IF_DV_2B;
        break;

    case INS_smov:
        assert(insOptsNone(opt));
        assert(isValidVectorElemsize(size));
        assert(size != EA_8BYTE);              // no encoding, use INS_umov
        assert(isGeneralRegister(reg1));
        assert(isVectorRegister(reg2));
        elemsize = size;
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        fmt = IF_DV_2B;
        break;

    case INS_add:
    case INS_sub:
        setFlags = false;
        isAddSub = true;
        break;

    case INS_adds:
    case INS_subs:
        setFlags = true;
        isAddSub = true;
        break;

    case INS_ldrsb:
    case INS_ldursb:
        // 'size' specifies how we sign-extend into 4 or 8 bytes of the target register
        assert(isValidGeneralDatasize(size));
        unscaledOp = (ins == INS_ldursb);
        scale = 0;
        isLdSt = true;
        break;

    case INS_ldrsh:
    case INS_ldursh:
        // 'size' specifies how we sign-extend into 4 or 8 bytes of the target register
        assert(isValidGeneralDatasize(size));
        unscaledOp = (ins == INS_ldursh);
        scale = 1;
        isLdSt = true;
        break;

    case INS_ldrsw:
    case INS_ldursw:
        // 'size' specifies how we sign-extend into 4 or 8 bytes of the target register
        assert(size == EA_8BYTE);
        unscaledOp = (ins == INS_ldursw);
        scale = 2;
        isLdSt = true;
        break;

    case INS_ldrb:
    case INS_strb:
        // size is ignored
        unscaledOp = false;
        scale = 0;
        isLdSt = true;
        break;

    case INS_ldurb:
    case INS_sturb:
        // size is ignored
        unscaledOp = true;
        scale = 0;
        isLdSt = true;
        break;

    case INS_ldrh:
    case INS_strh:
        // size is ignored
        unscaledOp = false;
        scale = 1;
        isLdSt = true;
        break;

    case INS_ldurh:
    case INS_sturh:
        // size is ignored
        unscaledOp = true;
        scale = 0;
        isLdSt = true;
        break;

    case INS_ldr:
    case INS_str:
        // Is the target a vector register?
        if (isVectorRegister(reg1))
        {
            assert(isValidVectorLSDatasize(size));
            assert(isGeneralRegisterOrSP(reg2));            
            isSIMD = true;
        }
        else
        {
            assert(isValidGeneralDatasize(size));            
        }
        unscaledOp = false;
        scale  = NaturalScale_helper(size);
        isLdSt = true;
        break;

    case INS_ldur:
    case INS_stur:
        // Is the target a vector register?
        if (isVectorRegister(reg1))
        { 
            assert(isValidVectorLSDatasize(size));
            assert(isGeneralRegisterOrSP(reg2));
            isSIMD = true;
        }
        else
        {
            assert(isValidGeneralDatasize(size));
        }
        unscaledOp = true;
        scale = 0;
        isLdSt = true;
        break;

    }  // end switch (ins)

    if (isLdSt)
    {
        assert(!isAddSub);

        if (isSIMD)
        {
            assert(isValidVectorLSDatasize(size));
            assert(isVectorRegister(reg1));
            assert((scale >= 0) && (scale <= 4));
        }
        else
        {
            assert(isValidGeneralLSDatasize(size));
            assert(isGeneralRegisterOrZR(reg1));
            assert((scale >= 0) && (scale <= 3));
        }

        assert(isGeneralRegisterOrSP(reg2));
        
        // Load/Store reserved encodings:
        if (insOptsIndexed(opt))
        {
            assert(reg1 != reg2);
        }

        reg2 = encodingSPtoZR(reg2);

        ssize_t  mask = (1 << scale) - 1;   // the mask of low bits that must be zero to encode the immediate
        if (imm == 0)
        {
            assert(insOptsNone(opt));  // PRE/POST Index doesn't make sense with an immediate of zero

            fmt = IF_LS_2A;
        }
        else if (insOptsIndexed(opt) || unscaledOp || (imm < 0) || ((imm & mask) != 0))
        {
            if ((imm >= -256) && (imm <= 255))
            {
                fmt = IF_LS_2C;
            }
            else
            {
                assert(!"Instruction cannot be encoded: IF_LS_2C");
            }            
        }
        else if (imm > 0)
        {
            assert(insOptsNone(opt));
            assert(!unscaledOp);

            if (((imm & mask) == 0) && ((imm >> scale) < 0x1000))
            {
                imm >>= scale;    // The immediate is scaled by the size of the ld/st

                fmt = IF_LS_2B;
            }
            else
            {
                assert(!"Instruction cannot be encoded: IF_LS_2B");
            }            
        }
    }
    else if (isAddSub)
    {
        assert(!isLdSt);
        assert(insOptsNone(opt));

        if (setFlags)           // Can't encode SP with setFlags
        {
            assert(isGeneralRegister(reg1));
            assert(isGeneralRegister(reg2));
        }
        else
        {
            assert(isGeneralRegisterOrSP(reg1));
            assert(isGeneralRegisterOrSP(reg2));

            // Is it just a mov?
            if (imm == 0)
            {
                // Is the mov even necessary?
                if (reg1 != reg2)
                {
                    emitIns_R_R(INS_mov, attr, reg1, reg2);
                }
                return;
            }

            reg1 = encodingSPtoZR(reg1);
            reg2 = encodingSPtoZR(reg2);
        }

        if (unsigned_abs(imm) <= 0x0fff)
        {
            if (imm < 0)
            {
                ins = insReverse(ins);
                imm = -imm;
            }
            assert(isValidUimm12(imm));
            fmt = IF_DI_2A;
        }
        else if (canEncodeWithShiftImmBy12(imm))     // Try the shifted by 12 encoding
        {
            // Encoding will use a 12-bit left shift of the immediate
            opt = INS_OPTS_LSL12;
            if (imm < 0)
            {
                ins = insReverse(ins);
                imm = -imm;
            }
            assert((imm & 0xfff) == 0);
            imm >>= 12;
            assert(isValidUimm12(imm));
            fmt = IF_DI_2A;
        }
        else
        {
            assert(!"Instruction cannot be encoded: IF_DI_2A");
        }
    }

    assert(fmt != IF_NONE);

    instrDesc *  id = emitNewInstrSC(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing three registers.
 */

void                emitter::emitIns_R_R_R(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg1,
                                           regNumber   reg2,
                                           regNumber   reg3,
                                           insOpts     opt)   /* = INS_OPTS_NONE */
{
    emitAttr   size     = EA_SIZE(attr);
    emitAttr   elemsize = EA_UNKNOWN;
    insFormat  fmt      = IF_NONE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_lsl:
    case INS_lsr:
    case INS_asr:
    case INS_ror:
    case INS_adc:
    case INS_adcs:
    case INS_sbc:
    case INS_sbcs:
    case INS_udiv:
    case INS_sdiv:
    case INS_mneg:
    case INS_smull:
    case INS_smnegl:
    case INS_smulh:
    case INS_umull:
    case INS_umnegl:
    case INS_umulh:
    case INS_lslv:
    case INS_lsrv:
    case INS_asrv:
    case INS_rorv:
        assert(insOptsNone(opt));
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3));
        fmt = IF_DR_3A;
        break;

    case INS_mul:
        if (insOptsNone(opt))
        {
            // general register
            assert(isValidGeneralDatasize(size));
            assert(isGeneralRegister(reg1));
            assert(isGeneralRegister(reg2));
            assert(isGeneralRegister(reg3));
            fmt = IF_DR_3A;
            break;
        }
        __fallthrough;

    case INS_mla:
    case INS_mls:
    case INS_pmul:
        assert(insOptsAnyArrangement(opt));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        assert(isValidVectorDatasize(size));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        if (ins == INS_pmul)
        {
            assert(elemsize == EA_1BYTE);    // only supports 8B or 16B
        }
        else // INS_mul, INS_mla, INS_mls
        {
            assert(elemsize != EA_8BYTE);    // can't use 2D or 1D
        }
        fmt = IF_DV_3A;
        break;

    case INS_add:
    case INS_sub:
        if (isVectorRegister(reg1))
        {
            assert(isVectorRegister(reg2));
            assert(isVectorRegister(reg3));

            if (insOptsAnyArrangement(opt))
            {
                // Vector operation
                assert(opt != INS_OPTS_1D);  // Reserved encoding
                assert(isValidVectorDatasize(size));
                assert(isValidArrangement(size, opt));
                fmt = IF_DV_3A;
            }
            else
            {
                // Scalar operation
                assert(insOptsNone(opt));
                assert(size == EA_8BYTE);
                fmt = IF_DV_3E;
            }
            break;
        }
        __fallthrough;

    case INS_adds:
    case INS_subs:
        emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0, INS_OPTS_NONE);
        return;

    case INS_saba:
    case INS_sabd:
    case INS_uaba:
    case INS_uabd:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        assert(insOptsAnyArrangement(opt));

        // Vector operation
        assert(isValidVectorDatasize(size));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(elemsize != EA_8BYTE);    // can't use 2D or 1D

        fmt = IF_DV_3A;
        break;

    case INS_mov:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(reg2 == reg3);
        assert(isValidVectorDatasize(size));
        // INS_mov is an alias for INS_orr (vector register)
        if (opt == INS_OPTS_NONE)
        {
            elemsize = EA_1BYTE;
            opt = optMakeArrangement(size, elemsize);
        }
        assert(isValidArrangement(size, opt));
        fmt = IF_DV_3C;
        break;

    case INS_and:
    case INS_bic:
    case INS_eor:
    case INS_orr:
    case INS_orn:
        if (isVectorRegister(reg1))
        { 
            assert(isValidVectorDatasize(size));
            assert(isVectorRegister(reg2));
            assert(isVectorRegister(reg3));
            if (opt == INS_OPTS_NONE)
            {
                elemsize = EA_1BYTE;
                opt = optMakeArrangement(size, elemsize);
            }
            assert(isValidArrangement(size, opt));
            fmt = IF_DV_3C;
            break;
        }
        __fallthrough;

    case INS_ands:
    case INS_bics:
    case INS_eon:
        emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0, INS_OPTS_NONE);
        return;

    case INS_bsl:
    case INS_bit:
    case INS_bif:
        assert(isValidVectorDatasize(size));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        if (opt == INS_OPTS_NONE)
        {
            elemsize = EA_1BYTE;
            opt = optMakeArrangement(size, elemsize);
        }
        assert(isValidArrangement(size, opt));
        fmt = IF_DV_3C;
        break;

    case INS_fadd:
    case INS_fsub:
    case INS_fdiv:
    case INS_fmax:
    case INS_fmin:
    case INS_fabd:
    case INS_fmul:
    case INS_fmulx:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_3B;
        }
        else
        {
            // Scalar operation
            assert(insOptsNone(opt));
            assert(isValidScalarDatasize(size));
            fmt = IF_DV_3D;
        }
        break;

    case INS_fnmul:
        // Scalar operation
        assert(insOptsNone(opt));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        assert(isValidScalarDatasize(size));
        fmt = IF_DV_3D;
        break;

    case INS_fmla:
    case INS_fmls:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        assert(insOptsAnyArrangement(opt));  // no scalar encoding, use 4-operand 'fmadd' or 'fmsub'

        // Vector operation
        assert(isValidVectorDatasize(size));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(isValidVectorElemsizeFloat(elemsize));
        assert(opt != INS_OPTS_1D);  // Reserved encoding
        fmt = IF_DV_3B;
        break;

    case INS_ldr:
    case INS_ldrb:
    case INS_ldrh:
    case INS_ldrsb:
    case INS_ldrsh:
    case INS_ldrsw:
    case INS_str:
    case INS_strb:
    case INS_strh:
        emitIns_R_R_R_Ext(ins, attr, reg1, reg2, reg3);
        return;

    case INS_ldp:
    case INS_ldpsw:
    case INS_ldnp:
    case INS_stp:
    case INS_stnp:
        emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0);
        return;

    }  // end switch (ins)

    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstr(attr);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing three registers and a constant.
 */

void                emitter::emitIns_R_R_R_I(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             regNumber   reg3,
                                             ssize_t     imm,
                                             insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size     = EA_SIZE(attr);
    emitAttr   elemsize = EA_UNKNOWN;
    insFormat  fmt      = IF_NONE;
    bool       isLdSt   = false;
    bool       isSIMD   = false;
    bool       isAddSub = false;
    bool       setFlags = false;
    unsigned   scale    = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_extr:
        assert(insOptsNone(opt));
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3));
        assert(isValidImmShift(imm, size));
        fmt = IF_DR_3E;
        break;

    case INS_and:
    case INS_ands:
    case INS_eor:
    case INS_orr:
    case INS_bic:
    case INS_bics:
    case INS_eon:
    case INS_orn:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3));
        assert(isValidImmShift(imm, size));
        if (imm == 0)
        {
            assert(insOptsNone(opt));       // a zero imm, means no shift kind
            fmt = IF_DR_3A;
        }
        else
        {
            assert(insOptsAnyShift(opt));   // a non-zero imm, must select shift kind
            fmt = IF_DR_3B;
        }
        break;

    case INS_fmul:  // by element, imm[0..3] selects the element of reg3
    case INS_fmla:
    case INS_fmls:
    case INS_fmulx:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        if (insOptsAnyArrangement(opt))
        {
            // Vector operation
            assert(isValidVectorDatasize(size));
            assert(isValidArrangement(size, opt));
            elemsize = optGetElemsize(opt);
            assert(isValidVectorElemsizeFloat(elemsize));
            assert(isValidVectorIndex(size, elemsize, imm));
            assert(opt != INS_OPTS_1D);  // Reserved encoding
            fmt = IF_DV_3BI;
        }
        else
        {
            // Scalar operation
            assert(insOptsNone(opt));
            assert(isValidScalarDatasize(size));
            elemsize = size;
            assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
            fmt = IF_DV_3DI;
        }
        break;

    case INS_mul:   // by element, imm[0..7] selects the element of reg3
    case INS_mla:
    case INS_mls:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        // Vector operation
        assert(insOptsAnyArrangement(opt));
        assert(isValidVectorDatasize(size));
        assert(isValidArrangement(size, opt));
        elemsize = optGetElemsize(opt);
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        // Only has encodings for H or S elemsize
        assert((elemsize == EA_2BYTE) || (elemsize == EA_4BYTE)); 
        // Only has encodings for V0..V15
        if ((elemsize == EA_2BYTE) &&  (reg3 >= REG_V16))
        {
            noway_assert(!"Invalid reg3");           
        }
        fmt = IF_DV_3AI;
        break;

    case INS_add:
    case INS_sub:
        setFlags = false;
        isAddSub = true;
        break;

    case INS_adds:
    case INS_subs:
        setFlags = true;
        isAddSub = true;
        break;
        
    case INS_ldpsw:
        scale = 2;
        isLdSt = true;
        break;

    case INS_ldnp:
    case INS_stnp:
        assert(insOptsNone(opt));   // Can't use Pre/Post index on these two instructions
        __fallthrough;

    case INS_ldp:
    case INS_stp:
        // Is the target a vector register?
        if (isVectorRegister(reg1))
        { 
            scale  = NaturalScale_helper(size);
            isSIMD = true;
        }
        else
        {
            scale = (size == EA_8BYTE) ? 3 : 2;
        }
        isLdSt = true;
        break;

    }  // end switch (ins)

    if (isLdSt)
    {
        assert(!isAddSub);
        assert(isGeneralRegisterOrSP(reg3));
        assert(insOptsNone(opt) || insOptsIndexed(opt));

        if (isSIMD)
        {
            assert(isValidVectorLSPDatasize(size));
            assert(isVectorRegister(reg1));
            assert(isVectorRegister(reg2));
            assert((scale >= 2) && (scale <= 4));
        }
        else
        {
            assert(isValidGeneralDatasize(size));
            assert(isGeneralRegisterOrZR(reg1));
            assert(isGeneralRegisterOrZR(reg2));
            assert((scale == 2) || (scale == 3));
        }

        // Load/Store Pair reserved encodings:
        if (emitInsIsLoad(ins))
        {
            assert(reg1 != reg2);
        }
        if (insOptsIndexed(opt))
        {
            assert(reg1 != reg3);
            assert(reg2 != reg3);
        }

        reg3 = encodingSPtoZR(reg3);

        ssize_t  mask = (1 << scale) - 1;   // the mask of low bits that must be zero to encode the immediate
        if (imm == 0)
        {
            assert(insOptsNone(opt));  // PRE/POST Index doesn't make sense with an immediate of zero

            fmt = IF_LS_3B;
        }
        else
        {
            if ((imm & mask) == 0)
            {
                imm >>= scale;    // The immediate is scaled by the size of the ld/st

                if ((imm >= -64) && (imm <= 63))
                {
                    fmt = IF_LS_3C;
                }
            }
#ifdef DEBUG
            if (fmt != IF_LS_3C)
            {
                assert(!"Instruction cannot be encoded: IF_LS_3C");
            }
#endif
        }
    }
    else if (isAddSub)
    {
        assert(!isLdSt);
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg3));
        
        if (setFlags || insOptsAluShift(opt))   // Can't encode SP in reg1 with setFlags or AluShift option
        {
            assert(isGeneralRegisterOrZR(reg1));
        }
        else
        {
            assert(isGeneralRegisterOrSP(reg1));
            reg1 = encodingSPtoZR(reg1);
        }

        if (insOptsAluShift(opt))               // Can't encode SP in reg2 with AluShift option
        {
            assert(isGeneralRegister(reg2));
        }
        else
        {
            assert(isGeneralRegisterOrSP(reg2));
            reg2 = encodingSPtoZR(reg2);
        }

        if (insOptsAnyExtend(opt))
        {
            assert((imm >= 0) && (imm <= 4));

            fmt = IF_DR_3C;
        }
        else if (insOptsAluShift(opt))
        {
            // imm should be non-zero and in [1..63]
            assert(isValidImmShift(imm, size) && (imm != 0));
            fmt = IF_DR_3B;
        }
        else if (imm == 0)
        {
            assert(insOptsNone(opt));

            fmt = IF_DR_3A;
        }
        else
        {
            assert(!"Instruction cannot be encoded: Add/Sub IF_DR_3A");
        }
    }
    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrCns(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing three registers, with an extend option 
 */

void                emitter::emitIns_R_R_R_Ext(instruction ins,
                                               emitAttr    attr,
                                               regNumber   reg1,
                                               regNumber   reg2,
                                               regNumber   reg3,
                                               insOpts     opt,         /* = INS_OPTS_NONE */
                                               int         shiftAmount) /* = -1 -- unset   */
{
    emitAttr   size   = EA_SIZE(attr);
    insFormat  fmt    = IF_NONE;
    bool       isSIMD = false;
    int        scale  = -1;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_ldrb:
    case INS_ldrsb:
    case INS_strb:
        scale = 0;
        break;

    case INS_ldrh:
    case INS_ldrsh:
    case INS_strh:
        scale = 1;
        break;

    case INS_ldrsw:
        scale = 2;
        break;

    case INS_ldr:
    case INS_str:
        // Is the target a vector register?
        if (isVectorRegister(reg1))
        { 
            assert(isValidVectorLSDatasize(size));
            scale  = NaturalScale_helper(size);
            isSIMD = true;
        }
        else
        {
            assert(isValidGeneralDatasize(size));
            scale = (size == EA_8BYTE) ? 3 : 2;
        }
        break;

    }  // end switch (ins)

    assert(scale != -1);
    assert(insOptsLSExtend(opt));

    if (isSIMD)
    {
        assert(isValidVectorLSDatasize(size));
        assert(isVectorRegister(reg1));
    }
    else
    {
        assert(isValidGeneralLSDatasize(size));
        assert(isGeneralRegisterOrZR(reg1));
    }

    assert(isGeneralRegisterOrSP(reg2));
    assert(isGeneralRegister(reg3));

    // Load/Store reserved encodings:
    if (insOptsIndexed(opt))
    {
        assert(reg1 != reg2);
    }

    if (shiftAmount == -1)
    { 
        shiftAmount = insOptsLSL(opt) ? scale : 0;
    }
    assert((shiftAmount == scale) || (shiftAmount == 0));

    reg2 = encodingSPtoZR(reg2);
    fmt  = IF_LS_3A;

    instrDesc *  id  = emitNewInstr(attr);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(opt);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);
    id->idReg3Scaled(shiftAmount == scale);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing two registers and two constants.
 */

void                emitter::emitIns_R_R_I_I(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             int         imm1,
                                             int         imm2)
{
    emitAttr   size     = EA_SIZE(attr);
    emitAttr   elemsize = EA_UNKNOWN;
    insFormat  fmt      = IF_NONE;
    size_t     immOut   = 0;   // composed from imm1 and imm2 and stored in the instrDesc

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
        int         lsb;
        int         width;
        bitMaskImm  bmi;

    case INS_bfm:
    case INS_sbfm:
    case INS_ubfm:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isValidImmShift(imm1, size));
        assert(isValidImmShift(imm2, size));
        bmi.immNRS = 0;
        bmi.immN = (size == EA_8BYTE);
        bmi.immR = imm1;
        bmi.immS = imm2;
        immOut = bmi.immNRS;
        fmt = IF_DI_2D;
        break;

    case INS_bfi:
    case INS_sbfiz:
    case INS_ubfiz:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        lsb   = getBitWidth(size) - imm1;
        width = imm2 - 1;
        assert(isValidImmShift(lsb, size));
        assert(isValidImmShift(width, size));
        bmi.immNRS = 0;
        bmi.immN = (size == EA_8BYTE);
        bmi.immR = lsb;
        bmi.immS = width;
        immOut = bmi.immNRS;
        fmt = IF_DI_2D;
        break;

    case INS_bfxil:
    case INS_sbfx:
    case INS_ubfx:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        lsb   = imm1;
        width = imm2 + imm1 - 1;
        assert(isValidImmShift(lsb, size));
        assert(isValidImmShift(width, size));
        bmi.immNRS = 0;
        bmi.immN = (size == EA_8BYTE);
        bmi.immR = imm1;
        bmi.immS = imm2 + imm1 - 1;
        immOut = bmi.immNRS;
        fmt = IF_DI_2D;
        break;

    case INS_mov:
    case INS_ins:
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        elemsize = size;
        assert(isValidVectorElemsize(elemsize));
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm1));
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm2));
        immOut = (imm1 << 4) + imm2;
        fmt = IF_DV_2F;
        break;

    }
    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSC(attr, immOut);

    id->idIns(ins);
    id->idInsFmt(fmt);

    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing four registers.
 */

void                emitter::emitIns_R_R_R_R(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             regNumber   reg3,
                                             regNumber   reg4)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_madd:
    case INS_msub:
    case INS_smaddl:
    case INS_smsubl:
    case INS_umaddl:
    case INS_umsubl:
        assert(isValidGeneralDatasize(size));
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3));
        assert(isGeneralRegister(reg4));
        fmt = IF_DR_4A;
        break;

    case INS_fmadd:
    case INS_fmsub:
    case INS_fnmadd:
    case INS_fnmsub:
        // Scalar operation
        assert(isValidScalarDatasize(size));
        assert(isVectorRegister(reg1));
        assert(isVectorRegister(reg2));
        assert(isVectorRegister(reg3));
        assert(isVectorRegister(reg4));
        fmt = IF_DV_4A;
        break;

    case INS_invalid:
        fmt = IF_NONE;
        break;
    }
    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstr(attr);

    id->idIns(ins);
    id->idInsFmt(fmt);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);
    id->idReg4(reg4);

    dispIns(id);
    appendToCurIG(id);
}



/*****************************************************************************
 *
 *  Add an instruction referencing a register and a condition code
 */

void                emitter::emitIns_R_COND(instruction ins,
                                            emitAttr    attr,
                                            regNumber   reg,
                                            insCond     cond)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    condFlagsImm cfi;
    cfi.immCFVal = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_cset:
    case INS_csetm:
        assert(isGeneralRegister(reg));
        cfi.cond = cond;
        fmt = IF_DR_1D;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);
    assert(isValidImmCond(cfi.immCFVal));

    instrDesc *  id  = emitNewInstrSC(attr, cfi.immCFVal);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing two registers and a condition code
 */

void                emitter::emitIns_R_R_COND(instruction ins,
                                              emitAttr    attr,
                                              regNumber   reg1,
                                              regNumber   reg2,
                                              insCond     cond)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    condFlagsImm cfi;
    cfi.immCFVal = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_cinc:
    case INS_cinv:
    case INS_cneg:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        cfi.cond  = cond;
        fmt = IF_DR_2D;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);
    assert(isValidImmCond(cfi.immCFVal));

    instrDesc *  id  = emitNewInstrSC(attr, cfi.immCFVal);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing two registers and a condition code
 */

void                emitter::emitIns_R_R_R_COND(instruction ins,
                                                emitAttr    attr,
                                                regNumber   reg1,
                                                regNumber   reg2,
                                                regNumber   reg3,
                                                insCond     cond)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    condFlagsImm cfi;
    cfi.immCFVal = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_csel:
    case INS_csinc:
    case INS_csinv:
    case INS_csneg:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3));
        cfi.cond  = cond;
        fmt = IF_DR_3D;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);
    assert(isValidImmCond(cfi.immCFVal));

    instrDesc *  id  = emitNewInstr(attr);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);
    id->idSmallCns(cfi.immCFVal);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing two registers the flags and a condition code
 */

void                emitter::emitIns_R_R_FLAGS_COND (instruction ins,
                                                     emitAttr    attr,
                                                     regNumber   reg1,
                                                     regNumber   reg2,
                                                     insCflags   flags,
                                                     insCond     cond)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    condFlagsImm cfi;
    cfi.immCFVal = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_ccmp:
    case INS_ccmn:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2));
        cfi.flags = flags;
        cfi.cond  = cond;
        fmt = IF_DR_2I;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);
    assert(isValidImmCondFlags(cfi.immCFVal));

    instrDesc *  id  = emitNewInstrSC(attr, cfi.immCFVal);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing a register, an immediate, the flags and a condition code
 */

void                emitter::emitIns_R_I_FLAGS_COND (instruction ins,
                                                     emitAttr    attr,
                                                     regNumber   reg,
                                                     int         imm,
                                                     insCflags   flags,
                                                     insCond     cond)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    condFlagsImm cfi;
    cfi.immCFVal = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_ccmp:
    case INS_ccmn:
        assert(isGeneralRegister(reg));
        if (imm < 0)
        {
            ins = insReverse(ins);
            imm = -imm;
        }
        if ((imm >= 0) && (imm <= 31))
        {
            cfi.imm5  = imm;
            cfi.flags = flags;
            cfi.cond  = cond;
            fmt = IF_DI_1F;
        }
        else
        {
            assert(!"Instruction cannot be encoded: ccmp/ccmn imm5");
        }
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);
    assert(isValidImmCondFlagsImm5(cfi.immCFVal));

    instrDesc *  id  = emitNewInstrSC(attr, cfi.immCFVal);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add a memory barrier instruction with a 'barrier' immediate 
 */

void            emitter::emitIns_BARR (instruction  ins,
                                       insBarrier   barrier)
{
    insFormat  fmt = IF_NONE;
    ssize_t    imm = 0;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_dsb:
    case INS_dmb:
    case INS_isb:

        fmt = IF_SI_0B;
        imm = (ssize_t) barrier;
        break;

    }  // end switch (ins)

    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrSC(EA_8BYTE, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction with a static data member operand. If 'size' is 0, the
 *  instruction operates on the address of the static member instead of its
 *  value (e.g. "push offset clsvar", rather than "push dword ptr [clsvar]").
 */

void                emitter::emitIns_C   (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          int          offs)
{
    NYI("emitIns_C");
}


/*****************************************************************************
 *
 *  Add an instruction referencing stack-based local variable.
 */

void                emitter::emitIns_S   (instruction ins,
                                          emitAttr    attr,
                                          int         varx,
                                          int         offs)
{
    NYI("emitIns_S");
}


/*****************************************************************************
 *
 *  Add an instruction referencing a register and a stack-based local variable.
 */
void                emitter::emitIns_R_S (instruction ins,
                                          emitAttr    attr,
                                          regNumber   reg1,
                                          int         varx,
                                          int         offs)
{
    emitAttr   size  = EA_SIZE(attr);
    insFormat  fmt   = IF_NONE;
    int        disp  = 0;
    unsigned   scale = 0;

    assert(offs >= 0);

    // TODO-ARM64-CQ: use unscaled loads?
    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_strb:
    case INS_ldrb:
    case INS_ldrsb:
        scale = 0;
        break;

    case INS_strh:
    case INS_ldrh:
    case INS_ldrsh:
        scale = 1;
        break;

    case INS_ldrsw:
        scale = 2;
        break;

    case INS_str:
    case INS_ldr:
        assert(isValidGeneralDatasize(size));
        scale = (size == EA_8BYTE) ? 3 : 2;
        break;

    case INS_lea:
        assert(size == EA_8BYTE);
        scale = 0;
        break;

    default:
        NYI("emitIns_R_S"); // FP locals?
        return;

    }  // end switch (ins)

    /* Figure out the variable's frame position */
    ssize_t imm;
    int  base;
    bool FPbased;

    base   = emitComp->lvaFrameAddress(varx, &FPbased);
    disp   = base + offs;
    assert((scale >= 0) && (scale <= 3));

    regNumber reg2 = FPbased ? REG_FPBASE : REG_SPBASE;
    reg2 = encodingSPtoZR(reg2);

    if (ins == INS_lea)
    {
        if (disp >= 0)
        {
            ins = INS_add;
            imm = disp;
        }
        else
        {
            ins = INS_sub;
            imm = -disp; 
        }

        if (imm <= 0x0fff)
        {
            fmt = IF_DI_2A;           // add reg1,reg2,#disp
        }
        else
        {
            regNumber rsvdReg = codeGen->rsGetRsvdReg();
            codeGen->instGen_Set_Reg_To_Imm(size, rsvdReg, imm);
            fmt = IF_DR_3A;           // add reg1,reg2,rsvdReg
        }
    }
    else
    {
        bool useRegForImm = false;
        ssize_t  mask = (1 << scale) - 1;   // the mask of low bits that must be zero to encode the immediate

        imm = disp;
        if (imm == 0)
        {
            fmt = IF_LS_2A;
        }
        else if ((imm < 0) || ((imm & mask) != 0))
        {
            if ((imm >= -256) && (imm <= 255))
            {
                fmt = IF_LS_2C;
            }
            else
            {
                useRegForImm = true;
            }            
        }
        else if (imm > 0)
        {
            if (((imm & mask) == 0) && ((imm >> scale) < 0x1000))
            {
                imm >>= scale;    // The immediate is scaled by the size of the ld/st

                fmt = IF_LS_2B;
            }
            else
            {
                useRegForImm = true;
            }            
        }

        if (useRegForImm)
        {
            regNumber rsvdReg = codeGen->rsGetRsvdReg();
            codeGen->instGen_Set_Reg_To_Imm(size, rsvdReg, imm);
            fmt = IF_LS_3A;
        }
    }

    assert(fmt != IF_NONE);

    instrDesc *  id = emitNewInstrCns(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
    id->idSetIsLclVar();

#ifdef DEBUG
    id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
#endif

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing a stack-based local variable and a register
 */
void                emitter::emitIns_S_R (instruction ins,
                                          emitAttr    attr,
                                          regNumber   reg1,
                                          int         varx,
                                          int         offs)
{
    assert(offs >= 0);
    emitAttr   size  = EA_SIZE(attr);
    insFormat  fmt   = IF_NONE;
    int        disp  = 0;
    unsigned   scale = 0;
    bool       isVectorStore = false;

    // TODO-ARM64-CQ: use unscaled loads?
    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_strb:
        scale = 0;
        assert(isGeneralRegisterOrZR(reg1));
        break;

    case INS_strh:
        scale = 1;
        assert(isGeneralRegisterOrZR(reg1));
        break;

    case INS_str:
        if (isGeneralRegisterOrZR(reg1))
        {
            assert(isValidGeneralDatasize(size));
            scale = (size == EA_8BYTE) ? 3 : 2;
        }
        else
        {
            assert(isVectorRegister(reg1));
            assert(isValidVectorLSDatasize(size));
            scale  = NaturalScale_helper(size);
            isVectorStore = true;
        }
        break;

    default:
        NYI("emitIns_S_R"); // FP locals?
        return;

    }  // end switch (ins)

    /* Figure out the variable's frame position */
    int  base;
    bool FPbased;

    base   = emitComp->lvaFrameAddress(varx, &FPbased);
    disp   = base + offs;
    assert(scale >= 0);
    if (isVectorStore)
    {
        assert(scale <= 4);
    }
    else
    {
       assert(scale <= 3);
    }

    // TODO-ARM64-CQ: with compLocallocUsed, should we use REG_SAVED_LOCALLOC_SP instead?
    regNumber reg2 = FPbased ? REG_FPBASE : REG_SPBASE;
    reg2 = encodingSPtoZR(reg2);

    bool useRegForImm = false;
    ssize_t imm = disp;
    ssize_t  mask = (1 << scale) - 1;   // the mask of low bits that must be zero to encode the immediate
    if (imm == 0)
    {
        fmt = IF_LS_2A;
    }
    else if ((imm < 0) || ((imm & mask) != 0))
    {
        if ((imm >= -256) && (imm <= 255))
        {
            fmt = IF_LS_2C;
        }
        else
        {
            useRegForImm = true;
        }            
    }
    else if (imm > 0)
    {
        if (((imm & mask) == 0) && ((imm >> scale) < 0x1000))
        {
            imm >>= scale;    // The immediate is scaled by the size of the ld/st

            fmt = IF_LS_2B;
        }
        else
        {
            useRegForImm = true;
        }            
    }

    if (useRegForImm)
    {
        // The reserved register is not stored in idReg3() since that field overlaps with iiaLclVar.
        // It is instead implicit when idSetIsLclVar() is set, with this encoding format.
        regNumber rsvdReg = codeGen->rsGetRsvdReg();
        codeGen->instGen_Set_Reg_To_Imm(size, rsvdReg, imm);
        fmt = IF_LS_3A;
    }

    assert(fmt != IF_NONE);

    instrDesc *  id  = emitNewInstrCns(attr, imm);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);

    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
    id->idSetIsLclVar();

#ifdef DEBUG
    id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
#endif

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing stack-based local variable and an immediate
 */
void                emitter::emitIns_S_I (instruction ins,
                                          emitAttr    attr,
                                          int         varx,
                                          int         offs,
                                          int         val)
{
    NYI("emitIns_S_I");
}


/*****************************************************************************
 *
 *  Add an instruction with a register + static member operands.
 */
void                emitter::emitIns_R_C (instruction  ins,
                                          emitAttr     attr,
                                          regNumber    reg,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          int          offs)
{
#if RELOC_SUPPORT
    // Static always need relocs
    if (!jitStaticFldIsGlobAddr(fldHnd))
        attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG);
#endif
    assert(offs >= 0);
    assert(instrDesc::fitsInSmallCns(offs));

    emitAttr      size = EA_SIZE(attr);
    insFormat     fmt  = IF_NONE;
    int           disp = 0;
    instrDescJmp* id   = emitNewInstrJmp();

    // TODO-ARM64-CQ: use unscaled loads?
    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_ldr:
        if (isVectorRegister(reg))
        {
            assert(isValidScalarDatasize(size));
        }
        else
        {
            assert(isGeneralRegister(reg));
            assert(isValidGeneralDatasize(size));
        }
        fmt = IF_LS_1A;
        break;

    default:
        break;
    }

    assert(fmt != IF_NONE);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsOpt(INS_OPTS_NONE);
    id->idSmallCns(offs);
    id->idOpSize(size);
    id->idAddr()->iiaFieldHnd = fldHnd;
    id->idSetIsBound();

    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);

}


/*****************************************************************************
 *
 *  Add an instruction with a static member + constant.
 */

void                emitter::emitIns_C_I (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          ssize_t      offs,
                                          ssize_t      val)
{
    NYI("emitIns_C_I");
}


/*****************************************************************************
 *
 *  Add an instruction with a static member + register operands.
 */

void                emitter::emitIns_C_R (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          regNumber    reg,
                                          int          offs)
{
    assert(!"emitIns_C_R not supported for RyuJIT backend");
}

void                emitter::emitIns_R_AR  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            int         offs,
                                            int         memCookie /* = 0 */,
                                            void *      clsCookie /* = NULL */)
{
    NYI("emitIns_R_AR");
}

void                emitter::emitIns_R_AI  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            ssize_t     disp)
{
    NYI("emitIns_R_AI");
}

void                emitter::emitIns_AR_R  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            int         offs,
                                            int         memCookie /* = 0 */,
                                            void *      clsCookie /* = NULL */)
{
    NYI("emitIns_AR_R");
}

void                emitter::emitIns_R_ARR (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            int         disp)
{
    NYI("emitIns_R_ARR");
}

void                emitter::emitIns_ARR_R (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            int         disp)
{
    NYI("emitIns_R_ARR");
}

void                emitter::emitIns_R_ARX (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            unsigned    mul,
                                            int         disp)
{
    NYI("emitIns_R_ARR");
}

/*****************************************************************************
 *
 *  Record that a jump instruction uses the short encoding
 *
 */
void  emitter::emitSetShortJump(instrDescJmp  * id)
{
    // All jumps are the same size.
    // NYI: support large conditional branches via pseudo-op.
    id->idjShort = true;
}

/*****************************************************************************
 *
 *  Add a label instruction.
 */

void                emitter::emitIns_R_L  (instruction   ins,
                                           emitAttr      attr,
                                           BasicBlock *  dst,
                                           regNumber     reg)
{
    assert(dst->bbFlags & BBF_JMP_TARGET);

    insFormat fmt = IF_NONE;

    instrDescJmp *id;
    switch (ins)
    {
    case INS_adr:
    case INS_adrp:
        fmt = IF_DI_1E;
        break;
    }
    assert(fmt == IF_DI_1E);

    id = emitNewInstrJmp();

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idAddr()->iiaBBlabel  = dst;
    id->idReg1(reg);
    id->idOpSize(EA_PTRSIZE);

#ifdef DEBUG
    // Mark the catch return
    if (emitComp->compCurBB->bbJumpKind == BBJ_EHCATCHRET)
    {
        id->idDebugOnlyInfo()->idCatchRet = true;
    }
#endif // DEBUG

    /* Assume the label reference will be long */

    id->idjShort              = 0;
    id->idjKeepLong           = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);
    
    /* Record the jump's IG and offset within it */

    id->idjIG                 = emitCurIG;
    id->idjOffs               = emitCurIGsize;

    /* Append this jump to this IG's jump list */

    id->idjNext               = emitCurIGjmpList;
                                emitCurIGjmpList = id;

#if EMITTER_STATS
    emitTotalIGjmps++;
#endif

    /* Figure out the max. size of the instruction */

    if  (!id->idjKeepLong)
    {
        // TODO-ARM64-NYI: handle large labels: larger than adr can do (+/-1MB)
    }

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add a data label instruction.
 */

void                emitter::emitIns_R_D  (instruction   ins,
                                           emitAttr      attr,
                                           unsigned      offs,
                                           regNumber     reg)
{
    NYI("emitIns_R_D");
}

void                emitter::emitIns_J_R   (instruction   ins,
                                            emitAttr      attr,
                                            BasicBlock *  dst,
                                            regNumber     reg)
{
    NYI("emitIns_J_R");
}

void                emitter::emitIns_J(instruction   ins,
                                       BasicBlock *  dst,
                                       int           instrCount)
{
    insFormat  fmt = IF_NONE;

    if (dst != nullptr)
    {
        assert(dst->bbFlags & BBF_JMP_TARGET);
    }
    else
    {
        assert(instrCount != 0);
    }

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_bl_local:
    case INS_b:
        fmt = IF_BI_0A;
        break;

    case INS_beq:
    case INS_bne:
    case INS_bhs:
    case INS_blo:
    case INS_bmi:
    case INS_bpl:
    case INS_bvs:
    case INS_bvc:
    case INS_bhi:
    case INS_bls:
    case INS_bge:
    case INS_blt:
    case INS_bgt:
    case INS_ble:
        // TODO-ARM64-CQ: fmt = IF_LARGEJMP;  /* Assume the jump will be long */
        fmt = IF_BI_0B;
        break;
    }
    assert((fmt == IF_BI_0A) ||
           (fmt == IF_BI_0B));

    instrDescJmp* id  = emitNewInstrJmp();

    id->idIns(ins);
    id->idInsFmt(fmt);

#ifdef DEBUG
    // Mark the finally call
    if (ins == INS_bl_local && emitComp->compCurBB->bbJumpKind == BBJ_CALLFINALLY)
    {
        id->idDebugOnlyInfo()->idFinallyCall = true;
    }
#endif // DEBUG

    /* Assume the jump will be long */

    // TODO-ARM64-Cleanup: there is only one size jump on ARM64. Clean this up.
    id->idjShort = 1;
    if (dst != nullptr)
    {
        id->idAddr()->iiaBBlabel = dst;
        id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);

#ifdef DEBUG
        if (emitComp->opts.compLargeBranches)        // Force long branches
            id->idjKeepLong = 1;
#endif // DEBUG
    }
    else
    {
        id->idAddr()->iiaSetInstrCount(instrCount);
        id->idjKeepLong = false;
        /* This jump must be short */
        emitSetShortJump(id);
        id->idSetIsBound();
    }

    /* Record the jump's IG and offset within it */

    id->idjIG                 = emitCurIG;
    id->idjOffs               = emitCurIGsize;

    /* Append this jump to this IG's jump list */

    id->idjNext               = emitCurIGjmpList;
                                emitCurIGjmpList = id;

#if EMITTER_STATS
    emitTotalIGjmps++;
#endif

    /* Figure out the max. size of the jump/call instruction */

    if  (!id->idjKeepLong)
    {
        // TODO-ARM64-NYI: handle large conditional branches: larger than b.cond can do (+/-1MB)
    }

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add a call instruction (direct or indirect).
 *      argSize<0 means that the caller will pop the arguments
 *
 * The other arguments are interpreted depending on callType as shown:
 * Unless otherwise specified, ireg,xreg,xmul,disp should have default values.
 *
 * EC_FUNC_TOKEN       : addr is the method address
 * EC_FUNC_ADDR        : addr is the absolute address of the function
 *
 * If callType is one of these emitCallTypes, addr has to be NULL.
 * EC_INDIR_R          : "call ireg".
 *
 * For ARM xreg, xmul and disp are never used and should always be 0/REG_NA.
 *
 *  Please consult the "debugger team notification" comment in genFnProlog().
 */

void                emitter::emitIns_Call(EmitCallType  callType,
                                          CORINFO_METHOD_HANDLE methHnd,
                                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)     // used to report call sites to the EE
                                          void*         addr,
                                          ssize_t       argSize,
                                          emitAttr      retSize,
                                          VARSET_VALARG_TP ptrVars,
                                          regMaskTP     gcrefRegs,
                                          regMaskTP     byrefRegs,
                                          IL_OFFSETX    ilOffset   /* = BAD_IL_OFFSET */,
                                          regNumber     ireg    /* = REG_NA */,
                                          regNumber     xreg    /* = REG_NA */,
                                          unsigned      xmul    /* = 0     */,
                                          ssize_t       disp    /* = 0     */,
                                          bool          isJump  /* = false */,
                                          bool          isNoGC  /* = false */,
                                          bool          isProfLeaveCB /* = false */)
{
    /* Sanity check the arguments depending on callType */

    assert(callType < EC_COUNT);
    assert((callType != EC_FUNC_TOKEN && callType != EC_FUNC_ADDR) ||
           (ireg == REG_NA && xreg == REG_NA && xmul == 0 && disp == 0));
    assert(callType < EC_INDIR_R || addr == NULL);
    assert(callType != EC_INDIR_R ||
           (ireg < REG_COUNT && xreg == REG_NA && xmul == 0 && disp == 0));

    // ARM never uses these
    assert(xreg == REG_NA && xmul == 0 && disp == 0);

    // Our stack level should be always greater than the bytes of arguments we push. Just
    // a sanity test.
    assert((unsigned) abs(argSize) <= codeGen->genStackLevel);

    int         argCnt;
    instrDesc*  id;

    /* This is the saved set of registers after a normal call */
    regMaskTP savedSet = RBM_CALLEE_SAVED;

    /* some special helper calls have a different saved set registers */

    if (isNoGC)
    {
        assert(emitNoGChelper(Compiler::eeGetHelperNum(methHnd)));

        // This call will preserve the liveness of most registers
        //
        // - On the ARM64 the NOGC helpers will preserve all registers,
        //   except for those listed in the RBM_CALLEE_TRASH_NOGC mask

        savedSet = RBM_ALLINT & ~RBM_CALLEE_TRASH_NOGC;

        // In case of Leave profiler callback, we need to preserve liveness of REG_PROFILER_RET_SCRATCH
        if (isProfLeaveCB)
        {
            savedSet |= RBM_PROFILER_RET_SCRATCH;
        }
    }
    else
    {
        assert(!emitNoGChelper(Compiler::eeGetHelperNum(methHnd)));
    }

    /* Trim out any callee-trashed registers from the live set */

    gcrefRegs &= savedSet;
    byrefRegs &= savedSet;

#ifdef  DEBUG
    if (EMIT_GC_VERBOSE)
    {
        printf("Call: GCvars=%s ", VarSetOps::ToString(emitComp, ptrVars));
        dumpConvertedVarSet(emitComp, ptrVars);
        printf(", gcrefRegs=");
        printRegMaskInt(gcrefRegs);
        emitDispRegSet(gcrefRegs);
        printf(", byrefRegs=");
        printRegMaskInt(byrefRegs);
        emitDispRegSet(byrefRegs);
        printf("\n");
    }
#endif

    assert(argSize % REGSIZE_BYTES == 0);
    argCnt = (int)(argSize / (int)sizeof(void*));
    
#ifdef DEBUGGING_SUPPORT
    /* Managed RetVal: emit sequence point for the call */
    if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET)
    {
        codeGen->genIPmappingAdd(ilOffset, false);
    }
#endif

    /*
        We need to allocate the appropriate instruction descriptor based
        on whether this is a direct/indirect call, and whether we need to
        record an updated set of live GC variables.
     */

    if  (callType >= EC_INDIR_R)
    {
        /* Indirect call, virtual calls */

        assert(callType == EC_INDIR_R);

        id  = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize);
    }
    else
    {
        /* Helper/static/nonvirtual/function calls (direct or through handle),
           and calls to an absolute addr. */

        assert(callType == EC_FUNC_TOKEN ||
               callType == EC_FUNC_ADDR);

        id  = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize);
    }

    /* Update the emitter's live GC ref sets */

    VarSetOps::Assign(emitComp, emitThisGCrefVars, ptrVars);
    emitThisGCrefRegs = gcrefRegs;
    emitThisByrefRegs = byrefRegs;

    /* Set the instruction - special case jumping a function */
    instruction ins;
    insFormat   fmt = IF_NONE;

    id->idSetIsNoGC(isNoGC);

    /* Record the address: method, indirection, or funcptr */

    if  (callType > EC_FUNC_ADDR)
    {
        /* This is an indirect call (either a virtual call or func ptr call) */

        switch (callType)
        {
        case EC_INDIR_R:            // the address is in a register

            id->idSetIsCallRegPtr();

            if (isJump)
            {
                ins = INS_br;      // INS_br  Reg
            }
            else
            {
                ins = INS_blr;     // INS_blr Reg
            }
            fmt = IF_BR_1A;

            id->idIns(ins);
            id->idInsFmt(fmt); 

            id->idReg1(ireg);
            assert(xreg == REG_NA);
            break;

        default:
            NO_WAY("unexpected instruction");
            break;
        }
    }
    else
    {
        /* This is a simple direct call: "call helper/method/addr" */

        assert(callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR);

        assert(addr != NULL);

        if (isJump)
        {
            ins = INS_b;      // INS_b imm28
            fmt = IF_BI_0A;
        }
        else
        {
            ins = INS_bl;     // INS_bl imm28
            fmt = IF_BI_0C;
        }

        id->idIns(ins);
        id->idInsFmt(fmt); 

        id->idAddr()->iiaAddr = (BYTE*)addr;

        if (callType == EC_FUNC_ADDR)
        {
            id->idSetIsCallAddr();
        }

#if RELOC_SUPPORT
        if (emitComp->opts.compReloc)
        {
            id->idSetIsDspReloc();
        }
#endif
    }

#ifdef  DEBUG
    if (EMIT_GC_VERBOSE)
    {
        if (id->idIsLargeCall())
        {
            printf("[%02u] Rec call GC vars = %s\n", id->idDebugOnlyInfo()->idNum, VarSetOps::ToString(emitComp, ((instrDescCGCA*)id)->idcGCvars));
        }
    }
#endif

#if defined(DEBUG) || defined(LATE_DISASM)
    id->idDebugOnlyInfo()->idMemCookie = (size_t)methHnd;    // method token
    id->idDebugOnlyInfo()->idClsCookie = 0;
    id->idDebugOnlyInfo()->idCallSig = sigInfo;
#endif

#if defined(LATE_DISASM)
    if (addr != nullptr)
    {
        codeGen->getDisAssembler().disSetMethod((size_t)addr, methHnd);
    }
#endif // defined(LATE_DISASM)

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Returns true if 'imm' is valid Cond encoding 
 */

/*static*/ bool             emitter::isValidImmCond(ssize_t imm)
{
    // range check the ssize_t value, to make sure it is a small unsigned value
    // and that only the bits in the cfi.cond are set
    if ((imm < 0) || (imm > 0xF))
        return false;

    condFlagsImm cfi;
    cfi.immCFVal = (unsigned) imm;

    return (cfi.cond <= INS_COND_LE);   // Don't allow 14 & 15 (AL & NV).
}

/*****************************************************************************
 *
 *  Returns true if 'imm' is valid Cond/Flags encoding 
 */

/*static*/ bool             emitter::isValidImmCondFlags(ssize_t imm)
{
    // range check the ssize_t value, to make sure it is a small unsigned value
    // and that only the bits in the cfi.cond or cfi.flags are set
    if ((imm < 0) || (imm > 0xFF))
        return false;

    condFlagsImm cfi;
    cfi.immCFVal = (unsigned) imm;

    return (cfi.cond <= INS_COND_LE);   // Don't allow 14 & 15 (AL & NV).
}

/*****************************************************************************
 *
 *  Returns true if 'imm' is valid Cond/Flags/Imm5 encoding 
 */

/*static*/ bool             emitter::isValidImmCondFlagsImm5(ssize_t imm)
{
    // range check the ssize_t value, to make sure it is a small unsigned value
    // and that only the bits in the cfi.cond, cfi.flags or cfi.imm5 are set
    if ((imm < 0) || (imm > 0x1FFF))
        return false;

    condFlagsImm cfi;
    cfi.immCFVal = (unsigned) imm;

    return (cfi.cond <= INS_COND_LE);   // Don't allow 14 & 15 (AL & NV).
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Rd' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Rd(regNumber reg)
{
    assert(isIntegerRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg;
    assert((ureg >=0) && (ureg <= 31));
    return ureg;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Rt' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Rt(regNumber reg)
{
    assert(isIntegerRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg;
    assert((ureg >=0) && (ureg <= 31));
    return ureg;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Rn' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Rn(regNumber reg)
{
    assert(isIntegerRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg;
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 5;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Rm' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Rm(regNumber reg)
{
    assert(isIntegerRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg;
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 16;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Ra' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Ra(regNumber reg)
{
    assert(isIntegerRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg;
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 10;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Vd' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Vd(regNumber reg)
{
    assert(emitter::isVectorRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg - (emitter::code_t) REG_V0; 
    assert((ureg >=0) && (ureg <= 31));
    return ureg;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Vt' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Vt(regNumber reg)
{
    assert(emitter::isVectorRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg - (emitter::code_t) REG_V0; 
    assert((ureg >=0) && (ureg <= 31));
    return ureg;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Vn' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Vn(regNumber reg)
{
    assert(emitter::isVectorRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg - (emitter::code_t) REG_V0; 
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 5;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Vm' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Vm(regNumber reg)
{
    assert(emitter::isVectorRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg - (emitter::code_t) REG_V0; 
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 16;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register used in the 'Va' position
 */

/*static*/ emitter::code_t  emitter::insEncodeReg_Va(regNumber reg)
{
    assert(emitter::isVectorRegister(reg));
    emitter::code_t ureg = (emitter::code_t) reg - (emitter::code_t) REG_V0; 
    assert((ureg >=0) && (ureg <= 31));
    return ureg << 10;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified condition code.
 */

/*static*/ emitter::code_t  emitter::insEncodeCond(insCond cond)
{
    emitter::code_t uimm = (emitter::code_t) cond;
    return uimm << 12;
}

/*****************************************************************************
 *
 *  Returns an encoding for the condition code with the lowest bit inverted (marked by invert(<cond>) in the architecture manual).
 */

/*static*/ emitter::code_t  emitter::insEncodeInvertedCond(insCond cond)
{
    emitter::code_t uimm = (emitter::code_t) cond;
    uimm ^= 1; // invert the lowest bit
    return uimm << 12;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified flags.
 */

/*static*/ emitter::code_t  emitter::insEncodeFlags(insCflags flags)
{
    emitter::code_t uimm = (emitter::code_t) flags;
    return uimm;
}

/*****************************************************************************
 *
 *  Returns the encoding for the Shift Count bits to be used for Arm64 encodings
 */

/*static*/ emitter::code_t  emitter::insEncodeShiftCount(ssize_t imm, emitAttr  size)
{
    assert((imm & 0x003F) == imm);
    assert(((imm & 0x0020) == 0) || (size == EA_8BYTE));

    return (emitter::code_t) imm << 10;
}

/*****************************************************************************
 *
 *  Returns the encoding to select a 64-bit datasize for an Arm64 instruction
 */

/*static*/ emitter::code_t  emitter::insEncodeDatasize(emitAttr  size)
{
    if (size == EA_8BYTE)
    {
        return 0x80000000;      // set the bit at location 31
    }
    else
    {
        assert(size == EA_4BYTE);
        return 0;
    }
}

/*****************************************************************************
 *
 *  Returns the encoding to select the datasize for the general load/store Arm64 instructions
 * 
 */

/*static*/ emitter::code_t  emitter::insEncodeDatasizeLS(emitter::code_t code, emitAttr  size)
{
    if (code & 0x00800000)        // Is this a sign-extending opcode? (i.e. ldrsw, ldrsh, ldrsb)
    {
        assert((size == EA_4BYTE) || (size == EA_8BYTE));
        if ((code & 0x80000000) == 0)  // Is it a ldrsh or ldrsb and not ldrsw ?
        {
            if (size == EA_4BYTE)      // Do we need to encode the 32-bit Rt size bit?
            {
                return 0x00400000;     // set the bit at location 22
            }
        }
    }
    else if (code & 0x80000000)   // Is this a ldr/str/ldur/stur opcode? 
    {
        assert((size == EA_4BYTE) || (size == EA_8BYTE));
        if (size == EA_8BYTE)          // Do we need to encode the 64-bit size bit?
        {
            return 0x40000000;         // set the bit at location 30
        }
    }
    return 0;
}

/*****************************************************************************
 *
 *  Returns the encoding to select the datasize for the vector load/store Arm64 instructions
 * 
 */

/*static*/ emitter::code_t  emitter::insEncodeDatasizeVLS(emitter::code_t code, emitAttr  size)
{
    code_t result = 0;

    // Check bit 29
    if ((code & 0x20000000) == 0) 
    {
        // LDR literal

        if (size == EA_16BYTE)
        {
            // set the operation size in bit 31
            result = 0x80000000;
        }
        else if (size == EA_8BYTE)
        {
            // set the operation size in bit 30
            result = 0x40000000;
        }
        else 
        {
            assert(size == EA_4BYTE);
            // no bits are set
            result = 0x00000000;
        }
    }
    else
    {
        // LDR non-literal
    
        if (size == EA_16BYTE)
        {
            // The operation size in bits 31 and 30 are zero
            // Bit 23 specifies a 128-bit Load/Store
            result = 0x00800000;
        }
        else if (size == EA_8BYTE)
        {
            // set the operation size in bits 31 and 30
            result = 0xC0000000;
        }
        else if (size == EA_4BYTE)
        {
            // set the operation size in bit 31
            result = 0x80000000;
        }
        else if (size == EA_2BYTE)
        {
            // set the operation size in bit 30
            result = 0x40000000;
        }
        else 
        {
            assert(size == EA_1BYTE);
            // The operation size in bits 31 and 30 are zero
            result = 0x00000000;
        }
    }

    // Or in bit 26 to indicate a Vector register is used as 'target'
    result |= 0x04000000;

    return result;
}

/*****************************************************************************
 *
 *  Returns the encoding to select the datasize for the vector load/store Arm64 instructions
 * 
 */

/*static*/ emitter::code_t  emitter::insEncodeDatasizeVPLS(emitter::code_t code, emitAttr  size)
{
    code_t result = 0;
    
    if (size == EA_16BYTE)
    {
        // The operation size in bits 31 and 30 are zero
        // Bit 23 specifies a 128-bit Load/Store
        result = 0x80000000;
    }
    else if (size == EA_8BYTE)
    {
        // set the operation size in bits 31 and 30
        result = 0x40000000;
    }
    else if (size == EA_4BYTE)
    {
        // set the operation size in bit 31
        result = 0x00000000;
    }

    // Or in bit 26 to indicate a Vector register is used as 'target'
    result |= 0x04000000;

    return result;
}

/*****************************************************************************
 *
 *  Returns the encoding to set the size bit and the N bits for a 'bitfield' instruction
 * 
 */

/*static*/ emitter::code_t  emitter::insEncodeDatasizeBF(emitter::code_t code, emitAttr  size)
{
    // is bit 30 equal to 0?
    if ((code & 0x40000000) == 0)   // is the opcode one of extr, sxtb, sxth or sxtw
    {
        if (size == EA_8BYTE)       // Do we need to set the sf and N bits?
        {
            return 0x80400000;      // set the sf-bit at location 31 and the N-bit at location 22
        }
    }
    return 0;                       // don't set any bits
}

/*****************************************************************************
 *
 *  Returns the encoding to select the 64/128-bit datasize for an Arm64 vector instruction
 */

/*static*/ emitter::code_t  emitter::insEncodeVectorsize(emitAttr  size)
{
    if (size == EA_16BYTE)
    {
        return 0x40000000;      // set the bit at location 30
    }
    else
    {
        assert(size == EA_8BYTE);
        return 0;
    }
}

/*****************************************************************************
 *
 *  Returns the encoding to select 'index' for an Arm64 vector elem instruction
 */
/*static*/ emitter::code_t  emitter::insEncodeVectorIndex(emitAttr elemsize, ssize_t index)
{
    code_t bits = (code_t) index;
    if (elemsize == EA_1BYTE)
    {
        bits <<= 1;
        bits |= 1;
    }
    else  if (elemsize == EA_2BYTE)
    {
        bits <<= 2;
        bits |= 2;
    }
    else  if (elemsize == EA_4BYTE)
    {
        bits <<= 3;
        bits |= 4;
    }
    else
    {
        assert(elemsize == EA_8BYTE);
        bits <<= 4;
        bits |= 8;
    }
    assert((bits >= 1) && (bits <= 0x1f));

    return (bits << 16);   // bits at locations [20,19,18,17,16]
}

/*****************************************************************************
 *
 *  Returns the encoding to select 'index2' for an Arm64 'ins' elem instruction
 */
/*static*/ emitter::code_t  emitter::insEncodeVectorIndex2(emitAttr elemsize, ssize_t index2)
{
    code_t bits = (code_t) index2;
    if (elemsize == EA_1BYTE)
    {
        // bits are correct
    }
    else  if (elemsize == EA_2BYTE)
    {
        bits <<= 1;
    }
    else  if (elemsize == EA_4BYTE)
    {
        bits <<= 2;
    }
    else
    {
        assert(elemsize == EA_8BYTE);
        bits <<= 3;
    }
    assert((bits >= 0) && (bits <= 0xf));

    return (bits << 11);   // bits at locations [14,13,12,11]
}

/*****************************************************************************
 *
 *  Returns the encoding to select the 'index' for an Arm64 'mul' by elem instruction
 */
/*static*/ emitter::code_t  emitter::insEncodeVectorIndexLMH(emitAttr elemsize, ssize_t index)
{
    code_t bits = 0;
    
    if (elemsize == EA_2BYTE)
    {
        assert((index >= 0) && (index <= 7));
        if (index & 0x4)
        {
            bits |= (1 << 11);  // set bit 11 'H'
        }
        if (index & 0x2)
        {
            bits |= (1 << 21);  // set bit 21 'L'
        }
        if (index & 0x1)
        {
            bits |= (1 << 20);  // set bit 20 'M'
        }
    }
    else  if (elemsize == EA_4BYTE)
    {
        assert((index >= 0) && (index <= 3));
        if (index & 0x2)
        {
            bits |= (1 << 11);  // set bit 11 'H'
        }
        if (index & 0x1)
        {
            bits |= (1 << 21);  // set bit 21 'L'
        }
    }
    else
    {
        assert(!"Invalid 'elemsize' value");
    }

    return bits;
}

/*****************************************************************************
 *
 *   Returns the encoding to shift by 'shift' for an Arm64 vector or scalar instruction
 */

/*static*/ emitter::code_t  emitter::insEncodeVectorShift(emitAttr size, ssize_t shift)
{
    assert(shift < getBitWidth(size));

    code_t imm = (code_t) (getBitWidth(size) + shift);

    return imm << 16;
}
  
/*****************************************************************************
 *
 *  Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 vector instruction
 */

/*static*/ emitter::code_t  emitter::insEncodeElemsize(emitAttr  size)
{
    if (size == EA_8BYTE)
    {
        return 0x00C00000;      // set the bit at location 23 and 22
    }
    else  if (size == EA_4BYTE)
    {
        return 0x00800000;      // set the bit at location 23 
    }
    else  if (size == EA_2BYTE)
    {
        return 0x00400000;      // set the bit at location 22
    }
    assert(size == EA_1BYTE);
    return 0x00000000; 
}

/*****************************************************************************
 *
 *  Returns the encoding to select the 4/8 byte elemsize for an Arm64 float vector instruction
 */

/*static*/ emitter::code_t  emitter::insEncodeFloatElemsize(emitAttr  size)
{
    if (size == EA_8BYTE)
    {
        return 0x00400000;      // set the bit at location 22
    }
    assert(size == EA_4BYTE);
    return 0x00000000; 
}

// Returns the encoding to select the index for an Arm64 float vector by elem instruction
/*static*/ emitter::code_t  emitter::insEncodeFloatIndex(emitAttr elemsize, ssize_t index)
{
    code_t result = 0x00000000;
    if (elemsize == EA_8BYTE)
    {
        assert((index >= 0) && (index <= 1));
        if (index == 1)
        {
            result |= 0x00000800;  // 'H' - set the bit at location 11
        }
    }
    else 
    {
        assert(elemsize == EA_4BYTE);
        assert((index >= 0) && (index <= 3));
        if (index & 2)
        {
            result |= 0x00000800;  // 'H' - set the bit at location 11
        }
        if (index & 1)
        {
            result |= 0x00200000;  // 'L' - set the bit at location 21
        }
    }
    return result;
}

/*****************************************************************************
 *
 *  Returns the encoding to select the fcvt operation for Arm64 instructions
 */
/*static*/ emitter::code_t  emitter::insEncodeConvertOpt(insFormat fmt, insOpts conversion)
{
    code_t result = 0;
    switch (conversion)
    {
    case INS_OPTS_S_TO_D:  // Single to Double
        assert(fmt == IF_DV_2J);
        result = 0x00008000;  // type=00, opc=01
        break;

    case INS_OPTS_D_TO_S:  // Double to Single
        assert(fmt == IF_DV_2J);
        result = 0x00400000;  // type=01, opc=00
        break;

    case INS_OPTS_H_TO_S:  // Half to Single
        assert(fmt == IF_DV_2J);
        result = 0x00C00000;  // type=11, opc=00
        break;

    case INS_OPTS_H_TO_D:  // Half to Double
        assert(fmt == IF_DV_2J);
        result = 0x00C08000;  // type=11, opc=01
        break;

    case INS_OPTS_S_TO_H:  // Single to Half
        assert(fmt == IF_DV_2J);
        result = 0x00018000;  // type=00, opc=11
        break;

    case INS_OPTS_D_TO_H:  // Double to Half
        assert(fmt == IF_DV_2J);
        result = 0x00418000;  // type=01, opc=11
        break;

    case INS_OPTS_S_TO_4BYTE:  // Single to INT32
        assert(fmt == IF_DV_2H);
        result = 0x00000000;  // sf=0, type=00
        break;

    case INS_OPTS_D_TO_4BYTE:  // Double to INT32
        assert(fmt == IF_DV_2H);
        result = 0x00400000;  // sf=0, type=01
        break;

    case INS_OPTS_S_TO_8BYTE:  // Single to INT64
        assert(fmt == IF_DV_2H);
        result = 0x80000000;  // sf=1, type=00
        break;

    case INS_OPTS_D_TO_8BYTE:  // Double to INT64
        assert(fmt == IF_DV_2H);
        result = 0x80400000;  // sf=1, type=01
        break;

    case INS_OPTS_4BYTE_TO_S:  // INT32 to Single
        assert(fmt == IF_DV_2I);
        result = 0x00000000;  // sf=0, type=00
        break;

    case INS_OPTS_4BYTE_TO_D:  // INT32 to Double
        assert(fmt == IF_DV_2I);
        result = 0x00400000;  // sf=0, type=01
        break;

    case INS_OPTS_8BYTE_TO_S:  // INT64 to Single
        assert(fmt == IF_DV_2I);
        result = 0x80000000;  // sf=1, type=00
        break;

    case INS_OPTS_8BYTE_TO_D:  // INT64 to Double
        assert(fmt == IF_DV_2I);
        result = 0x80400000;  // sf=1, type=01
        break;

    default:
        assert(!"Invalid 'conversion' value");
        break;
    }
    return result;
}

/*****************************************************************************
 *
 *  Returns the encoding to have the Rn register be updated Pre/Post indexed 
 *  or not updated
 */

/*static*/ emitter::code_t  emitter::insEncodeIndexedOpt(insOpts opt)
{
    assert(emitter::insOptsNone(opt) || emitter::insOptsIndexed(opt));

    if (emitter::insOptsIndexed(opt))
    {
        if (emitter::insOptsPostIndex(opt))
        {
            return 0x00000400;      // set the bit at location 10
        }
        else
        { 
            assert(emitter::insOptsPreIndex(opt));
            return 0x00000C00;      // set the bit at location 10 and 11
        }
    }
    else
    {
        assert(emitter::insOptsNone(opt));
        return 0;                   // bits 10 and 11 are zero
    }
}

/*****************************************************************************
 *
 *  Returns the encoding for a ldp/stp instruction to have the Rn register
 *  be updated Pre/Post indexed or not updated 
 */

/*static*/ emitter::code_t  emitter::insEncodePairIndexedOpt(instruction ins, insOpts opt)
{
    assert(emitter::insOptsNone(opt) || emitter::insOptsIndexed(opt));

    if ((ins == INS_ldnp) || (ins == INS_stnp))
    {
        assert(emitter::insOptsNone(opt));
        return 0;                        // bits 23 and 24 are zero
    }
    else
    {
        if (emitter::insOptsIndexed(opt))
        {
            if (emitter::insOptsPostIndex(opt))
            {
                return 0x00800000;       // set the bit at location 23
            }
            else
            { 
                assert(emitter::insOptsPreIndex(opt));
                return 0x01800000;       // set the bit at location 24 and 23
            }
        }
        else
        {
            assert(emitter::insOptsNone(opt));
            return 0x01000000;           // set the bit at location 24
        }
    }
}

/*****************************************************************************
 *
 *  Returns the encoding to apply a Shift Type on the Rm register
 */

/*static*/ emitter::code_t  emitter::insEncodeShiftType(insOpts opt)
{
    if (emitter::insOptsNone(opt))
    {
        // None implies the we encode LSL (with a zero immediate)
        opt = INS_OPTS_LSL;
    }
    assert(emitter::insOptsAnyShift(opt));

    emitter::code_t option = (emitter::code_t) opt - (emitter::code_t) INS_OPTS_LSL;
    assert(option <= 3);

    return option << 22;        // bits 23, 22
}

/*****************************************************************************
 *
 *  Returns the encoding to apply a 12 bit left shift to the immediate
 */

/*static*/ emitter::code_t  emitter::insEncodeShiftImm12(insOpts opt)
{
    if (emitter::insOptsLSL12(opt))
    {
        return 0x00400000;           // set the bit at location 22
    }
    return 0;
}

/*****************************************************************************
 *
 *  Returns the encoding to have the Rm register use an extend operation 
 */

/*static*/ emitter::code_t  emitter::insEncodeExtend(insOpts opt)
{
    if (emitter::insOptsNone(opt) || (opt == INS_OPTS_LSL))
    {
        // None or LSL implies the we encode UXTX
        opt = INS_OPTS_UXTX;
    }
    assert(emitter::insOptsAnyExtend(opt));

    emitter::code_t option = (emitter::code_t) opt - (emitter::code_t) INS_OPTS_UXTB;
    assert(option <= 7);

    return option << 13;        // bits 15,14,13
}

/*****************************************************************************
 *
 *  Returns the encoding to scale the Rm register by {0,1,2,3,4} 
 *  when using an extend operation 
 */

/*static*/ emitter::code_t  emitter::insEncodeExtendScale(ssize_t imm)
{
    assert((imm >= 0) && (imm <= 4));

    return (emitter::code_t) imm << 10; // bits 12,11,10
}


/*****************************************************************************
 *
 *  Returns the encoding to have the Rm register be auto scaled by the ld/st size 
 */

/*static*/ emitter::code_t  emitter::insEncodeReg3Scale(bool isScaled)
{
    if (isScaled)
    {
        return 0x00001000;      // set the bit at location 12
    }
    else
    {
        return 0;
    }
}

/*****************************************************************************
 *
 *  Output a local jump or other instruction with a pc-relative immediate.
 *  Note that this may be invoked to overwrite an existing jump instruction at 'dst'
 *  to handle forward branch patching.
 */

BYTE*               emitter::emitOutputLJ(insGroup  *ig, BYTE *dst, instrDesc *i)
{
    instrDescJmp *  id   = (instrDescJmp*)i;

    unsigned        srcOffs;
    unsigned        dstOffs;
    BYTE *          srcAddr;
    BYTE *          dstAddr;
    ssize_t         distVal;
    ssize_t         loBits;

    instruction     ins       = id->idIns();
    insFormat       fmt       = id->idInsFmt();
    code_t          code      = emitInsCode(ins, fmt);   // Basic instruction encoding

    bool            loadLabel = false;
    bool            isJump    = false;

    switch (ins)
    {
    default:
        isJump = true;
        break;

    case INS_tbz:
    case INS_tbnz:
    case INS_cbz:
    case INS_cbnz:
        isJump = true;
        break;

    case INS_ldr:
    case INS_ldrsw:
    case INS_adr:
        loadLabel = true;
        break;
    }

    /* Figure out the distance to the target */

    srcOffs = emitCurCodeOffs(dst);
    srcAddr = emitOffsetToPtr(srcOffs);

    if (id->idAddr()->iiaHasInstrCount())
    {
        assert(ig != NULL);
        int      instrCount = id->idAddr()->iiaGetInstrCount();
        unsigned insNum     = emitFindInsNum(ig, id);
        if (instrCount < 0)
        {
            // Backward branches using instruction count must be within the same instruction group.
            assert(insNum + 1 >= (unsigned)(-instrCount));
        }
        dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount));
        dstAddr = emitOffsetToPtr(dstOffs);
    }
    else if (id->idAddr()->iiaIsJitDataOffset())
    {
        assert(loadLabel);

        /* This is actually a reference to the JIT data section */

        int doff = id->idAddr()->iiaGetJitDataOffset();
        assert(doff >= 0);
        ssize_t imm = emitGetInsSC(id);
        assert((imm >= 0) && (imm < 0x1000));   // 0x1000 is arbitrary, currently 'imm' is always 0

        unsigned dataOffs = (unsigned) (doff + imm);
        assert(dataOffs < emitDataSize());
        dstAddr = emitDataOffsetToPtr(dataOffs);
        dstOffs = (unsigned) ((ssize_t) (dstAddr - srcAddr) + srcOffs);
        assert((dstOffs & 3) == 0);
        assert(isValidSimm19(dstOffs));
    }
    else
    {
        dstOffs = id->idAddr()->iiaIGlabel->igOffs;
        dstAddr = emitOffsetToPtr(dstOffs);
    }

    distVal = (ssize_t) (dstAddr - srcAddr);

    if  (dstOffs <= srcOffs)
    {
        /* This is a backward jump - distance is known at this point */

#if     DEBUG_EMIT
        if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
        {
            size_t      blkOffs = id->idjIG->igOffs;

            if  (INTERESTING_JUMP_NUM == 0)
            printf("[3] Jump %u:\n", id->idDebugOnlyInfo()->idNum);
            printf("[3] Jump  block is at %08X - %02X = %08X\n", blkOffs, emitOffsAdj, blkOffs - emitOffsAdj);
            printf("[3] Jump        is at %08X - %02X = %08X\n", srcOffs, emitOffsAdj, srcOffs - emitOffsAdj);
            printf("[3] Label block is at %08X - %02X = %08X\n", dstOffs, emitOffsAdj, dstOffs - emitOffsAdj);
        }
#endif
    }
    else
    {
        /* This is a  forward jump - distance will be an upper limit */

        emitFwdJumps  = true;

        /* The target offset will be closer by at least 'emitOffsAdj', but only if this
           jump doesn't cross the hot-cold boundary. */

        if (!emitJumpCrossHotColdBoundary(srcOffs, dstOffs))
        {
            dstOffs -= emitOffsAdj;
            distVal -= emitOffsAdj;
        }

        /* Record the location of the jump for later patching */

        id->idjOffs = dstOffs;

        /* Are we overflowing the id->idjOffs bitfield? */
        if (id->idjOffs != dstOffs)
            IMPL_LIMITATION("Method is too large");       

#if     DEBUG_EMIT
        if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
        {
            size_t      blkOffs = id->idjIG->igOffs;

            if  (INTERESTING_JUMP_NUM == 0)
            printf("[4] Jump %u:\n", id->idDebugOnlyInfo()->idNum);
            printf("[4] Jump  block is at %08X\n"              , blkOffs);
            printf("[4] Jump        is at %08X\n"              , srcOffs);
            printf("[4] Label block is at %08X - %02X = %08X\n", dstOffs + emitOffsAdj, emitOffsAdj, dstOffs);
        }
#endif

    }

#ifdef  DEBUG
    if (0 && emitComp->verbose)
    {
        size_t  sz          = 4;
        int     distValSize = id->idjShort ? 4 : 8;
        printf("; %s jump [%08X/%03u] from %0*X to %0*X: dist = %08XH\n",
            (dstOffs <= srcOffs)?"Fwd":"Bwd", dspPtr(id), id->idDebugOnlyInfo()->idNum,
            distValSize, srcOffs + sz,
            distValSize, dstOffs,
            distVal);
    }
#endif

    loBits = (distVal & 3);
    distVal >>= 2; // branch offset encodings are scaled by 4.

    /* For forward jumps, record the address of the distance value */
    id->idjTemp.idjAddr = (distVal > 0) ? dst : NULL;

    assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false);
    assert(insOptsNone(id->idInsOpt()));

    if (isJump)
    {
        assert(!id->idjKeepLong);

        // branch offsets must be a multiple of 4
        noway_assert(loBits == 0);

        if (fmt == IF_BI_0A)
        {
            // INS_b or INS_bl_local
            noway_assert(isValidSimm26(distVal));
            distVal &= 0x3FFFFFFLL;
            code |= distVal;
        }
        else if (fmt == IF_BI_0B)
        {
            // INS_beq, INS_bne, etc...
            noway_assert(isValidSimm19(distVal));
            distVal &= 0x7FFFFLL;
            code |= distVal << 5;
        }
        else if (fmt == IF_BI_1A)  // BI_1A   X.......iiiiiiii iiiiiiiiiiittttt      Rt simm19:00
        {
            // INS_cbz or INS_cbnz
            code |= insEncodeDatasize(id->idOpSize());    // X 
            code |= insEncodeReg_Rt(id->idReg1());        // ttttt

            noway_assert(isValidSimm19(distVal));
            distVal &= 0x7FFFFLL;                         // 19 bits 
            code |= distVal << 5;
        }
        else if (fmt == IF_BI_1B)  // BI_1B   B.......bbbbbiii iiiiiiiiiiittttt      Rt imm6, simm14:00
        {
            // INS_tbz or INS_tbnz
            ssize_t imm = emitGetInsSC(id);
            assert(isValidImmShift(imm, id->idOpSize())); 

            if (imm & 0x20)                               // test bit 32-63 ?
            {
                code |= 0x80000000;                       // B 
            }
            code |= ((imm & 0x1F) << 19);                 // bbbbb
            code |= insEncodeReg_Rt(id->idReg1());        // ttttt

            noway_assert(isValidSimm14(distVal));
            distVal &= 0x3FFFLL;                          // 14 bits 
            code |= distVal << 5;
        }
        else
        {
            assert(!"Unknown fmt");
        }
    }
    else if (loadLabel)
    {
        if (fmt == IF_LS_1A)       // LS_1A   XX......iiiiiiii iiiiiiiiiiittttt      Rt simm21
        {
            // INS_ldr or INS_ldrsw (PC-Relative)

            // Is the target a vector register?
            if (isVectorRegister(id->idReg1()))
            { 
                code &= 0x3FFFFFFF;                                  // clear the size bits
                code |= insEncodeDatasizeVLS(code, id->idOpSize());  // XX
                code |= insEncodeReg_Vt(id->idReg1());               // ttttt
            }
            else
            {
                code |= insEncodeReg_Rt(id->idReg1());               // ttttt
            }

            noway_assert(loBits == 0);
            noway_assert(isValidSimm19(distVal));
            distVal &= 0x7FFFFLL;                         // 19 bits 
            code |= distVal <<  5;
        }
        else if (fmt == IF_DI_1E)  // DI_1E   .ii.....iiiiiiii iiiiiiiiiiiddddd      Rd simm21
        {
            // INS_adr or INS_adrp
            code |= insEncodeReg_Rd(id->idReg1());        // ddddd

            noway_assert(isValidSimm19(distVal));
            distVal &= 0x7FFFFLL;                         // 19 bits 
            code |= distVal <<  5;
            code |= loBits  << 29;                        //  2 bits
        }
        else
        {
            assert(!"Unknown fmt");
        }
    }

    dst  += emitOutput_Instr(dst, code);

    return  dst;
}

/*****************************************************************************
 *
 *  Output a call instruction.
 */

unsigned emitter::emitOutputCall(insGroup  *ig, BYTE *dst, instrDesc *id, code_t code)
{
    const unsigned char callInstrSize = sizeof(code_t);  // 4 bytes
    regMaskTP     gcrefRegs;
    regMaskTP     byrefRegs;

    VARSET_TP VARSET_INIT_NOCOPY(GCvars, VarSetOps::UninitVal());

    // Is this a "fat" call descriptor? 
    if  (id->idIsLargeCall())
    {
        instrDescCGCA* idCall = (instrDescCGCA*) id;
        gcrefRegs   = idCall->idcGcrefRegs;
        byrefRegs   = idCall->idcByrefRegs;
        VarSetOps::Assign(emitComp, GCvars, idCall->idcGCvars);
    }
    else
    {
        assert(!id->idIsLargeDsp());
        assert(!id->idIsLargeCns());

        gcrefRegs   = emitDecodeCallGCregs(id);
        byrefRegs   = 0;
        VarSetOps::AssignNoCopy(emitComp, GCvars, VarSetOps::MakeEmpty(emitComp));
    }

    /* We update the GC info before the call as the variables cannot be
        used by the call. Killing variables before the call helps with
        boundary conditions if the call is CORINFO_HELP_THROW - see bug 50029.
        If we ever track aliased variables (which could be used by the
        call), we would have to keep them alive past the call. */

    emitUpdateLiveGCvars(GCvars, dst);

    // Now output the call instruction and update the 'dst' pointer
    //
    unsigned outputInstrSize = emitOutput_Instr(dst, code);
    dst += outputInstrSize;

    // All call instructions are 4-byte in size on ARM64
    //
    assert(outputInstrSize == callInstrSize);

    // If the method returns a GC ref, mark R0 appropriately.
    if       (id->idGCref() == GCT_GCREF)
    {
        gcrefRegs |= RBM_INTRET;
    }
    else if  (id->idGCref() == GCT_BYREF)
    {
        byrefRegs |= RBM_INTRET;
    }

    // If the GC register set has changed, report the new set.
    if  (gcrefRegs != emitThisGCrefRegs)
    {
        emitUpdateLiveGCregs(GCT_GCREF, gcrefRegs, dst);
    }
    // If the Byref register set has changed, report the new set.
    if  (byrefRegs != emitThisByrefRegs)
    {
        emitUpdateLiveGCregs(GCT_BYREF, byrefRegs, dst);
    }

    // Some helper calls may be marked as not requiring GC info to be recorded.
    if  ((!id->idIsNoGC()))
    {
        // On ARM64, as on AMD64, we don't change the stack pointer to push/pop args.
        // So we're not really doing a "stack pop" here (note that "args" is 0), but we use this mechanism
        // to record the call for GC info purposes.  (It might be best to use an alternate call,
        // and protect "emitStackPop" under the EMIT_TRACK_STACK_DEPTH preprocessor variable.)
        emitStackPop(dst, /*isCall*/true, callInstrSize, /*args*/0);
      
        // Do we need to record a call location for GC purposes?
        //
        if  (!emitFullGCinfo)
        {
            emitRecordGCcall(dst, callInstrSize);
        }
    }
    return callInstrSize;
}

/*****************************************************************************
 *
 *  Emit a 32-bit Arm64 instruction
 */

/*static*/ unsigned emitter::emitOutput_Instr(BYTE *dst, code_t code)
{
    assert(sizeof(code_t) == 4);
    *((code_t *) dst) = code;

    return sizeof(code_t);
}

/*****************************************************************************
*
 *  Append the machine code corresponding to the given instruction descriptor
 *  to the code block at '*dp'; the base of the code block is 'bp', and 'ig'
 *  is the instruction group that contains the instruction. Updates '*dp' to
 *  point past the generated code, and returns the size of the instruction
 *  descriptor in bytes.
 */

size_t              emitter::emitOutputInstr(insGroup  *ig,
                                             instrDesc *id, BYTE **dp)
{
    BYTE    *       dst  = *dp;
    BYTE    *       odst = dst;
    code_t          code = 0;
    size_t          sz   = emitGetInstrDescSize(id); // TODO-ARM64-Cleanup: on ARM, this is set in each case. why?
    instruction     ins  = id->idIns();
    insFormat       fmt  = id->idInsFmt();
    emitAttr        size = id->idOpSize();
    unsigned char   callInstrSize = 0;
    unsigned        condcode;

#ifdef  DEBUG
#if     DUMP_GC_TABLES
    bool            dspOffs = emitComp->opts.dspGCtbls;
#else
    bool            dspOffs = !emitComp->opts.disDiffable;
#endif
#endif // DEBUG

    assert(REG_NA == (int)REG_NA);

    VARSET_TP VARSET_INIT_NOCOPY(GCvars, VarSetOps::UninitVal());

    /* What instruction format have we got? */

    switch (fmt)
    {
        ssize_t   imm;
        ssize_t   index;
        ssize_t   index2;
        unsigned  scale;
        unsigned  cmode;
        unsigned  immShift;
        bool      hasShift;
        emitAttr  extSize;
        emitAttr  elemsize;
        emitAttr  datasize;

    case IF_BI_0A:    // BI_0A   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
    case IF_BI_0B:    // BI_0B   ......iiiiiiiiii iiiiiiiiiii.....               simm19:00
        assert(id->idGCref() == GCT_NONE);
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz = sizeof(instrDescJmp);
        break;

    case IF_BI_0C:    // BI_0C   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
        code  = emitInsCode(ins, fmt);
        sz    = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc);
        dst  += emitOutputCall(ig, dst, id, code);
        // Always call RecordRelocation so that we wire in a JumpStub when we don't reach
        emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_BRANCH26);
        break;

    case IF_BI_1A:    // BI_1A   ......iiiiiiiiii iiiiiiiiiiittttt      Rt       simm19:00
        assert(insOptsNone(id->idInsOpt()));
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz = sizeof(instrDescJmp);
        break;

    case IF_BI_1B:    // BI_1B   B.......bbbbbiii iiiiiiiiiiittttt      Rt imm6, simm14:00
        assert(insOptsNone(id->idInsOpt()));
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz = sizeof(instrDescJmp);
        break;

    case IF_BR_1A:    // BR_1A   ................ ......nnnnn.....         Rn
        assert(insOptsNone(id->idInsOpt()));
        code  = emitInsCode(ins, fmt);
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn

        if (ins == INS_ret)
        {
            dst  += emitOutput_Instr(dst, code);
        }
        else  // INS_blr or INS_br
        {
             sz    = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc);
             dst  += emitOutputCall(ig, dst, id, code);
        }
        break;

    case IF_LS_1A:    // LS_1A   XX......iiiiiiii iiiiiiiiiiittttt      Rt    PC imm(1MB)
        assert(insOptsNone(id->idInsOpt()));
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz = sizeof(instrDescJmp);
        break;

    case IF_LS_2A:    // LS_2A   .X.......X...... ......nnnnnttttt      Rt Rn
        assert(insOptsNone(id->idInsOpt()));
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVLS(code, id->idOpSize());  // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
        }
        else
        {
            code |= insEncodeDatasizeLS(code, id->idOpSize());   // .X.......X
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
        }
        code |= insEncodeReg_Rn(id->idReg2());                   // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_LS_2B:    // LS_2B   .X.......Xiiiiii iiiiiinnnnnttttt      Rt Rn    imm(0-4095)
        assert(insOptsNone(id->idInsOpt()));
        imm   = emitGetInsSC(id);
        assert(isValidUimm12(imm));
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVLS(code, id->idOpSize());  // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
        }
        else
        {
            code |= insEncodeDatasizeLS(code, id->idOpSize());   // .X.......X
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
        }
        code |= ((code_t) imm << 10);                            // iiiiiiiiiiii
        code |= insEncodeReg_Rn(id->idReg2());                   // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_LS_2C:    // LS_2C   .X.......X.iiiii iiiiPPnnnnnttttt      Rt Rn    imm(-256..+255) no/pre/post inc
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        imm   = emitGetInsSC(id);
        assert((imm >= -256) && (imm <= 255));  // signed 9 bits
        imm  &= 0x1ff;                          // force into unsigned 9 bit representation
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVLS(code, id->idOpSize());  // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
        }
        else
        {
            code |= insEncodeDatasizeLS(code, id->idOpSize());   // .X.......X
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
        }
        code |= insEncodeIndexedOpt(id->idInsOpt());             // PP
        code |= ((code_t) imm << 12);                            // iiiiiiiii
        code |= insEncodeReg_Rn(id->idReg2());                   // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_LS_3A:    // LS_3A   .X.......X.mmmmm oooS..nnnnnttttt      Rt Rn Rm ext(Rm) LSL {}
        assert(insOptsLSExtend(id->idInsOpt()));
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVLS(code, id->idOpSize());  // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
        }
        else
        {
            code |= insEncodeDatasizeLS(code, id->idOpSize());   // .X.......X
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
        }
        code |= insEncodeExtend(id->idInsOpt());                 // ooo
        code |= insEncodeReg_Rn(id->idReg2());                   // nnnnn
        if (id->idIsLclVar())
        {
            code |= insEncodeReg_Rm(codeGen->rsGetRsvdReg());    // mmmmm
        }
        else
        {
            code |= insEncodeReg3Scale(id->idReg3Scaled());      // S
            code |= insEncodeReg_Rm(id->idReg3());               // mmmmm
        }
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_LS_3B:    // LS_3B   X............... .aaaaannnnnddddd      Rd Ra Rn
        assert(insOptsNone(id->idInsOpt()));
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVPLS(code, id->idOpSize()); // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
            code |= insEncodeReg_Va(id->idReg2());               // aaaaa
        }
        else
        {
            code |= insEncodeDatasize(id->idOpSize());           // X 
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
            code |= insEncodeReg_Ra(id->idReg2());               // aaaaa
        }
        code |= insEncodeReg_Rn(id->idReg3());                   // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_LS_3C:    // LS_3C   X......PP.iiiiii iaaaaannnnnddddd      Rd Ra Rn imm(im7,sh)
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        imm   = emitGetInsSC(id);
        assert((imm >= -64) && (imm <= 63));    // signed 7 bits
        imm  &= 0x7f;                           // force into unsigned 7 bit representation
        code  = emitInsCode(ins, fmt);
        // Is the target a vector register?
        if (isVectorRegister(id->idReg1()))
        { 
            code &= 0x3FFFFFFF;                                  // clear the size bits
            code |= insEncodeDatasizeVPLS(code, id->idOpSize()); // XX
            code |= insEncodeReg_Vt(id->idReg1());               // ttttt
            code |= insEncodeReg_Va(id->idReg2());               // aaaaa
        }
        else
        {
            code |= insEncodeDatasize(id->idOpSize());           // X 
            code |= insEncodeReg_Rt(id->idReg1());               // ttttt
            code |= insEncodeReg_Ra(id->idReg2());               // aaaaa
        }
        code |= insEncodePairIndexedOpt(ins, id->idInsOpt());    // PP
        code |= ((code_t) imm << 15);                            // iiiiiiiii
        code |= insEncodeReg_Rn(id->idReg3());                   // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_1A:    // DI_1A   X.......shiiiiii iiiiiinnnnn.....         Rn    imm(i12,sh)
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL12(id->idInsOpt()));
        imm   = emitGetInsSC(id);
        assert(isValidUimm12(imm));
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeShiftImm12(id->idInsOpt());         // sh
        code |= ((code_t) imm << 10);                        // iiiiiiiiiiii
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_1B:    // DI_1B   X........hwiiiii iiiiiiiiiiiddddd      Rd       imm(i16,hw)
        imm   = emitGetInsSC(id);
        assert(isValidImmHWVal(imm, id->idOpSize()));
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= ((code_t) imm << 5);                         // hwiiiii iiiiiiiiiii
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_1C:    // DI_1C   X........Nrrrrrr ssssssnnnnn.....         Rn    imm(N,r,s)
        imm   = emitGetInsSC(id);
        assert(isValidImmNRS(imm, id->idOpSize()));
        code  = emitInsCode(ins, fmt);
        code |= ((code_t) imm << 10);                        // Nrrrrrrssssss
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_1D:    // DI_1D   X........Nrrrrrr ssssss.....ddddd      Rd       imm(N,r,s)
        imm   = emitGetInsSC(id);
        assert(isValidImmNRS(imm, id->idOpSize()));
        code  = emitInsCode(ins, fmt);
        code |= ((code_t) imm << 10);                        // Nrrrrrrssssss
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_1E:    // DI_1E   .ii.....iiiiiiii iiiiiiiiiiiddddd      Rd       simm21
        assert(insOptsNone(id->idInsOpt()));
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz = sizeof(instrDescJmp);
        break;

    case IF_DI_1F:    // DI_1F   X..........iiiii cccc..nnnnn.nzcv      Rn imm5  nzcv cond
        imm   = emitGetInsSC(id);
        assert(isValidImmCondFlagsImm5(imm));
        {
            condFlagsImm  cfi;
            cfi.immCFVal = (unsigned) imm;
            code  = emitInsCode(ins, fmt);
            code |= insEncodeDatasize(id->idOpSize());       // X
            code |= insEncodeReg_Rn(id->idReg1());           // nnnnn
            code |= ((code_t) cfi.imm5 << 16);               // iiiii
            code |= insEncodeFlags(cfi.flags);               // nzcv
            code |= insEncodeCond(cfi.cond);                 // cccc
            dst  += emitOutput_Instr(dst, code);
        }
        break;

    case IF_DI_2A:    // DI_2A   X.......shiiiiii iiiiiinnnnnddddd      Rd Rn    imm(i12,sh)
        assert(insOptsNone(id->idInsOpt()) || insOptsLSL12(id->idInsOpt()));
        imm   = emitGetInsSC(id);
        assert(isValidUimm12(imm));
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeShiftImm12(id->idInsOpt());         // sh
        code |= ((code_t) imm << 10);                        // iiiiiiiiiiii
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_2B:    // DI_2B   X.........Xnnnnn ssssssnnnnnddddd      Rd Rn    imm(0-63)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert(isValidImmShift(imm, id->idOpSize()));
        code |= insEncodeDatasizeBF(code, id->idOpSize());   // X........X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg2());               // Reg2 also in mmmmm
        code |= insEncodeShiftCount(imm, id->idOpSize());    // ssssss
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_2C:    // DI_2C   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imm(N,r,s)
        imm   = emitGetInsSC(id);
        assert(isValidImmNRS(imm, id->idOpSize()));
        code  = emitInsCode(ins, fmt);
        code |= ((code_t) imm << 10);                        // Nrrrrrrssssss
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DI_2D:    // DI_2D   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imr, imms   (N,r,s)
        if (ins == INS_asr || ins == INS_lsl || ins == INS_lsr)
        {
            imm = emitGetInsSC(id);
            assert(isValidImmShift(imm, id->idOpSize()));

            // Shift immediates are aliases of the SBFM/UBFM instructions 
            // that actually take 2 registers and 2 constants, 
            // Since we stored the shift immediate value 
            // we need to calculate the N,R and S values here.

            bitMaskImm  bmi;
            bmi.immNRS = 0;

            bmi.immN = (size == EA_8BYTE) ? 1 : 0;
            bmi.immR = imm;
            bmi.immS = (size == EA_8BYTE) ? 0x3f : 0x1f;

            // immR and immS are now set correctly for INS_asr and INS_lsr
            // but for INS_lsl we have to adjust the values for immR and immS
            //
            if (ins == INS_lsl)
            {
                bmi.immR = -imm & bmi.immS;
                bmi.immS = bmi.immS - imm;
            }

            // setup imm with the proper 13 bit value N:R:S
            //
            imm = bmi.immNRS;            
        }
        else
        {
            // The other instructions have already have encoded N,R and S values
            imm = emitGetInsSC(id);
        }
        assert(isValidImmNRS(imm, id->idOpSize()));

        code = emitInsCode(ins, fmt);
        code |= ((code_t)imm << 10);                         // Nrrrrrrssssss
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_1D:    // DR_1D   X............... cccc.......ddddd      Rd       cond
        imm   = emitGetInsSC(id);
        assert(isValidImmCond(imm));
        {
            condFlagsImm  cfi;
            cfi.immCFVal = (unsigned) imm;
            code  = emitInsCode(ins, fmt);
            code |= insEncodeDatasize(id->idOpSize());       // X
            code |= insEncodeReg_Rd(id->idReg1());           // ddddd
            code |= insEncodeInvertedCond(cfi.cond);         // cccc
            dst  += emitOutput_Instr(dst, code);
        }
        break;

    case IF_DR_2A:    // DR_2A   X..........mmmmm ......nnnnn.....         Rn Rm
        assert(insOptsNone(id->idInsOpt()));
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2B:    // DR_2B   X.......sh.mmmmm ssssssnnnnn.....         Rn Rm {LSL,LSR,ASR,ROR} imm(0-63)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert(isValidImmShift(imm, id->idOpSize()));
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeShiftType(id->idInsOpt());          // sh
        code |= insEncodeShiftCount(imm, id->idOpSize());    // ssssss
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2C:    // DR_2C   X..........mmmmm ooosssnnnnn.....         Rn Rm ext(Rm) LSL imm(0-4)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert((imm >= 0) && (imm <= 4));                    // imm [0..4]
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeExtend(id->idInsOpt());             // ooo
        code |= insEncodeExtendScale(imm);                   // sss
        code |= insEncodeReg_Rn(id->idReg1());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2D:    // DR_2D   X..........nnnnn cccc..nnnnnddddd      Rd Rn    cond
        imm   = emitGetInsSC(id);
        assert(isValidImmCond(imm));
        {
            condFlagsImm  cfi;
            cfi.immCFVal = (unsigned) imm;
            code  = emitInsCode(ins, fmt);
            code |= insEncodeDatasize(id->idOpSize());       // X
            code |= insEncodeReg_Rd(id->idReg1());           // ddddd
            code |= insEncodeReg_Rn(id->idReg2());           // nnnnn
            code |= insEncodeReg_Rm(id->idReg2());           // mmmmm   
            code |= insEncodeInvertedCond(cfi.cond);         // cccc
            dst  += emitOutput_Instr(dst, code);
        }
        break;

    case IF_DR_2E:    // DR_2E   X..........mmmmm ...........ddddd      Rd    Rm
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2F:    // DR_2F   X.......sh.mmmmm ssssss.....ddddd      Rd    Rm {LSL,LSR,ASR} imm(0-63)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert(isValidImmShift(imm, id->idOpSize()));
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeShiftType(id->idInsOpt());          // sh
        code |= insEncodeShiftCount(imm, id->idOpSize());    // ssssss
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2G:    // DR_2G   X............... .....xnnnnnddddd      Rd Rn
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        if (ins == INS_rev) 
        {
            if (size == EA_8BYTE)
            {
                code |= 0x00000400;                          // x - bit at location 10
            }            
        }
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2H:    // DR_2H   X........X...... ......nnnnnddddd      Rd Rn
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasizeBF(code, id->idOpSize());   // X........X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_2I:    // DR_2I   X..........mmmmm cccc..nnnnn.nzcv      Rn Rm    nzcv cond
        imm   = emitGetInsSC(id);
        assert(isValidImmCondFlags(imm));
        {
            condFlagsImm  cfi;
            cfi.immCFVal = (unsigned) imm;
            code  = emitInsCode(ins, fmt);
            code |= insEncodeDatasize(id->idOpSize());       // X
            code |= insEncodeReg_Rn(id->idReg1());           // nnnnn
            code |= insEncodeReg_Rm(id->idReg2());           // mmmmm
            code |= insEncodeFlags(cfi.flags);               // nzcv
            code |= insEncodeCond(cfi.cond);                 // cccc
            dst  += emitOutput_Instr(dst, code);
        }
        break;

    case IF_DR_3A:    // DR_3A   X..........mmmmm ......nnnnnmmmmm      Rd Rn Rm
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        if (id->idIsLclVar())
        {
            code |= insEncodeReg_Rm(codeGen->rsGetRsvdReg()); // mmmmm
        }
        else
        {
            code |= insEncodeReg_Rm(id->idReg3());           // mmmmm
        }
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_3B:    // DR_3B   X.......sh.mmmmm ssssssnnnnnddddd      Rd Rn Rm {LSL,LSR,ASR} imm(0-63)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert(isValidImmShift(imm, id->idOpSize()));
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg3());               // mmmmm
        code |= insEncodeShiftType(id->idInsOpt());          // sh
        code |= insEncodeShiftCount(imm, id->idOpSize());    // ssssss
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_3C:    // DR_3C   X..........mmmmm ooosssnnnnnddddd      Rd Rn Rm ext(Rm) LSL imm(0-4)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert((imm >= 0) && (imm <= 4));                    // imm [0..4]
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeExtend(id->idInsOpt());             // ooo
        code |= insEncodeExtendScale(imm);                   // sss
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_3D:    // DR_3D   X..........mmmmm cccc..nnnnnddddd      Rd Rn Rm cond
        imm   = emitGetInsSC(id);
        assert(isValidImmCond(imm));
        {
            condFlagsImm  cfi;
            cfi.immCFVal = (unsigned) imm;
            code  = emitInsCode(ins, fmt);
            code |= insEncodeDatasize(id->idOpSize());       // X
            code |= insEncodeReg_Rd(id->idReg1());           // ddddd
            code |= insEncodeReg_Rn(id->idReg2());           // nnnnn
            code |= insEncodeReg_Rm(id->idReg3());           // mmmmm   
            code |= insEncodeCond(cfi.cond);                 // cccc
            dst  += emitOutput_Instr(dst, code);
        }
        break;

    case IF_DR_3E:    // DR_3E   X........X.mmmmm ssssssnnnnnddddd      Rd Rn Rm imm(0-63)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        assert(isValidImmShift(imm, id->idOpSize()));
        code |= insEncodeDatasizeBF(code, id->idOpSize());   // X........X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg3());               // mmmmm
        code |= insEncodeShiftCount(imm, id->idOpSize());    // ssssss
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DR_4A:    // DR_4A   X..........mmmmm .aaaaannnnnmmmmm      Rd Rn Rm Ra
        code  = emitInsCode(ins, fmt);
        code |= insEncodeDatasize(id->idOpSize());           // X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Rm(id->idReg3());               // mmmmm
        code |= insEncodeReg_Ra(id->idReg4());               // aaaaa
        dst  += emitOutput_Instr(dst, code);
        break;
        
    case IF_DV_1A:    // DV_1A   .........X.iiiii iii........ddddd      Vd imm8    (fmov - immediate scalar)
        imm   = emitGetInsSC(id);
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= ((code_t) imm << 13);                        // iiiii iii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_1B:    // DV_1B   .QX..........iii cmod..iiiiiddddd      Vd imm8    (immediate vector)
        imm      = emitGetInsSC(id) & 0x0ff;
        immShift = (emitGetInsSC(id) & 0x700) >> 8;
        elemsize = optGetElemsize(id->idInsOpt());
        cmode    = 0;
        switch (elemsize)
        {                                  // cmode
        case EA_1BYTE:
            cmode = 0xE;                   // 1110
            break;
        case EA_2BYTE:
            cmode  = 0x8;
            cmode |= (immShift << 1);      // 10x0
            break;
        case EA_4BYTE:
            if (immShift < 4)
            {
                cmode = 0x0;
                cmode |= (immShift << 1);  // 0xx0
            }
            else  // MSL
            {
                cmode = 0xC;
                if (immShift & 2)
                    cmode |= 1;            // 110x
            }
            break;
        case EA_8BYTE:
            cmode = 0xE;                   // 1110
            break;
        }

        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        if ((ins == INS_fmov) || (ins== INS_movi))
        {
            if (elemsize == EA_8BYTE)
            {
                code |= 0x20000000;                          // X
            }
        }
        if (ins != INS_fmov)
        {
            assert((cmode >= 0) && (cmode <= 0xF));
            code |= (cmode << 12);                           // cmod
        }
        code |= (((code_t) imm >> 5)   << 16);               // iii
        code |= (((code_t) imm & 0x1f) <<  5);               // iiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_1C:    // DV_1C   .........X...... ......nnnnn.....      Vn #0.0    (fcmp - with zero)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vn(id->idReg1());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2A:    // DV_2A   .Q.......X...... ......nnnnnddddd      Vd Vn      (fabs, fcvt - vector)
        elemsize = optGetElemsize(id->idInsOpt());
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2B:    // DV_2B   .Q.........iiiii ......nnnnnddddd      Rd Vn[] (umov/smov    - to general)
        elemsize = id->idOpSize();
        index    = emitGetInsSC(id);
        datasize = (elemsize == EA_8BYTE) ? EA_16BYTE : EA_8BYTE;
        if (ins == INS_smov)
        {
            datasize = EA_16BYTE;
        }
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(datasize);               // Q
        code |= insEncodeVectorIndex(elemsize, index);       // iiiii
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2C:    // DV_2C   .Q.........iiiii ......nnnnnddddd      Vd Rn   (dup/ins - vector from general)
        if (ins == INS_dup) 
        {
            datasize = id->idOpSize();
            elemsize = optGetElemsize(id->idInsOpt());
            index    = 0;
        }
        else  // INS_ins
        {
            datasize = EA_16BYTE;
            elemsize = id->idOpSize();
            index    = emitGetInsSC(id);
        }
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(datasize);               // Q
        code |= insEncodeVectorIndex(elemsize, index);       // iiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2D:    // DV_2D   .Q.........iiiii ......nnnnnddddd      Vd Vn[]   (dup - vector)
        index = emitGetInsSC(id);
        elemsize = optGetElemsize(id->idInsOpt());
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeVectorIndex(elemsize, index);       // iiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2E:    // DV_2E   ...........iiiii ......nnnnnddddd      Vd Vn[]   (dup - scalar)
        index = emitGetInsSC(id);
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorIndex(elemsize, index);       // iiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2F:    // DV_2F   ...........iiiii .jjjj.nnnnnddddd      Vd[] Vn[] (ins - element)
        elemsize = id->idOpSize();
        imm   = emitGetInsSC(id);
        index  = (imm >> 4) & 0xf;
        index2 = imm & 0xf;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorIndex(elemsize,  index);      // iiiii
        code |= insEncodeVectorIndex2(elemsize, index2);     // jjjj
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2G:    // DV_2G   .........X...... ......nnnnnddddd      Vd Vn      (fmov,fcvtXX - register)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2H:    // DV_2H   X........X...... ......nnnnnddddd      Rd Vn      (fmov - to general)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeConvertOpt(fmt, id->idInsOpt());    // X   X
        code |= insEncodeReg_Rd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2I:    // DV_2I   X........X...... ......nnnnnddddd      Vd Rn      (fmov - from general)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeConvertOpt(fmt, id->idInsOpt());    // X   X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Rn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2J:    // DV_2J   ........SS.....D D.....nnnnnddddd      Vd Vn      (fcvt)
        code  = emitInsCode(ins, fmt);
        code |= insEncodeConvertOpt(fmt, id->idInsOpt());    // SS DD
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2K:    // DV_2K   .........X.mmmmm ......nnnnn.....      Vn Vm      (fcmp)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vn(id->idReg1());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg2());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2L:    // DV_2L   ........XX...... ......nnnnnddddd      Vd Vn      (abs, neg - scalar)
        elemsize = id->idOpSize();
        code  = emitInsCode(ins, fmt);
        code |= insEncodeElemsize(elemsize);                 // XX
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2M:    // DV_2M   .Q......XX...... ......nnnnnddddd      Vd Vn      (abs, neg   - vector)
        elemsize = optGetElemsize(id->idInsOpt());
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeElemsize(elemsize);                 // XX
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;
 
   case IF_DV_2N:    // DV_2N   .........iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - scalar)
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorShift(EA_8BYTE, imm);         // iiiiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_2O:    // DV_2O   .Q.......iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - vector)
        imm   = emitGetInsSC(id);
        elemsize = optGetElemsize(id->idInsOpt());        
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeVectorShift(elemsize, imm);         // iiiiiii
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3A:    // DV_3A   .Q......XX.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        code  = emitInsCode(ins, fmt);
        elemsize = optGetElemsize(id->idInsOpt());
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeElemsize(elemsize);                 // XX
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3AI:   // DV_3AI  .Q......XXLMmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        elemsize = optGetElemsize(id->idInsOpt());
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeElemsize(elemsize);                 // XX
        code |= insEncodeVectorIndexLMH(elemsize, imm);      // LM H
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3B:    // DV_3B   .Q.......X.mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        code  = emitInsCode(ins, fmt);
        elemsize = optGetElemsize(id->idInsOpt());
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3BI:   // DV_3BI  .Q.......XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        elemsize = optGetElemsize(id->idInsOpt());
        assert(isValidVectorIndex(id->idOpSize(), elemsize, imm));
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeFloatIndex(elemsize, imm);          // L H
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3C:    // DV_3C   .Q.........mmmmm ......nnnnnddddd      Vd Vn Vm   (vector)
        code  = emitInsCode(ins, fmt);
        code |= insEncodeVectorsize(id->idOpSize());         // Q
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3D:    // DV_3D   .........X.mmmmm ......nnnnnddddd      Vd Vn Vm   (scalar)
        code  = emitInsCode(ins, fmt);
        code |= insEncodeFloatElemsize(id->idOpSize());      // X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3DI:   // DV_3DI  .........XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (scalar by elem)
        code  = emitInsCode(ins, fmt);
        imm   = emitGetInsSC(id);
        elemsize = id->idOpSize();
        assert(isValidVectorIndex(EA_16BYTE, elemsize, imm));
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeFloatIndex(elemsize, imm);          // L H
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_3E:    // DV_3E   ...........mmmmm ......nnnnnddddd      Vd Vn Vm   (scalar)
        code  = emitInsCode(ins, fmt);
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_DV_4A:    // DV_4A   .........X.mmmmm .aaaaannnnnddddd      Vd Va Vn Vm (scalar)
        code  = emitInsCode(ins, fmt);
        elemsize = id->idOpSize();
        code |= insEncodeFloatElemsize(elemsize);            // X
        code |= insEncodeReg_Vd(id->idReg1());               // ddddd
        code |= insEncodeReg_Vn(id->idReg2());               // nnnnn
        code |= insEncodeReg_Vm(id->idReg3());               // mmmmm
        code |= insEncodeReg_Va(id->idReg4());               // aaaaa
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_SN_0A:    // SN_0A   ................ ................      
        code  = emitInsCode(ins, fmt);
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_SI_0A:    // SI_0A   ...........iiiii iiiiiiiiiii.....               imm16
        imm   = emitGetInsSC(id);
        assert(isValidUimm16(imm));
        code  = emitInsCode(ins, fmt);
        code |= ((code_t) imm << 5);                         // iiiii iiiiiiiiiii
        dst  += emitOutput_Instr(dst, code);
        break;

    case IF_SI_0B:    // SI_0B   ................ ....bbbb........               imm4 - barrier
        imm   = emitGetInsSC(id);
        assert((imm >= 0) && (imm <= 15));
        code  = emitInsCode(ins, fmt);
        code |= ((code_t) imm << 8);                         // bbbb
        dst  += emitOutput_Instr(dst, code);
        break;

    default:
        assert(!"Unexpected format");
        break;
    }

    // Determine if any registers now hold GC refs, or whether a register that was overwritten held a GC ref.
    // We assume here that "id->idGCref()" is not GC_NONE only if the instruction described by "id" writes a
    // GC ref to register "id->idReg1()".  (It may, apparently, also not be GC_NONE in other cases, such as
    // for stores, but we ignore those cases here.)
    if (emitInsMayWriteToGCReg(id))  // True if "id->idIns()" writes to a register than can hold GC ref.
    {
        // If we ever generate instructions that write to multiple registers,
        // then we'd need to more work here to ensure that changes in the status of GC refs are
        // tracked properly.
        if (emitInsMayWriteMultipleRegs(id))
        {
            // INS_ldp etc... 
            // We assume that "idReg1" and "idReg2" are the destination register for all instructions 
            emitGCregDeadUpd(id->idReg1(), dst);
            emitGCregDeadUpd(id->idReg2(), dst);
        }
        else
        {
            // We assume that "idReg1" is the destination register for all instructions 
            if (id->idGCref() != GCT_NONE)
            {
                emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst);
            }
            else
            {
                emitGCregDeadUpd(id->idReg1(), dst);
            }
        }
    }

    // Now we determine if the instruction has written to a (local variable) stack location, and either written a GC ref or
    // overwritten one.
    if (emitInsWritesToLclVarStackLoc(id))
    {
        int varNum = id->idAddr()->iiaLclVar.lvaVarNum();
        unsigned ofs    = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), sizeof(size_t));
        bool FPbased;
        int adr = emitComp->lvaFrameAddress(varNum, &FPbased);
        if (id->idGCref() != GCT_NONE)
        {
            emitGCvarLiveUpd(adr + ofs, varNum, id->idGCref(), dst);
        }
        else
        {
            // If the type of the local is a gc ref type, update the liveness.
            var_types vt;
            if (varNum >= 0)
            {
                // "Regular" (non-spill-temp) local.
                vt = var_types(emitComp->lvaTable[varNum].lvType);
            }
            else
            {
                TempDsc* tmpDsc = emitComp->tmpFindNum(varNum);
                vt = tmpDsc->tdTempType();
            }
            if (vt == TYP_REF || vt == TYP_BYREF)
                emitGCvarDeadUpd(adr + ofs, dst);
        }
    }

#ifdef  DEBUG
    /* Make sure we set the instruction descriptor size correctly */

    size_t expected = emitSizeOfInsDsc(id);
    assert(sz == expected);

    if  (emitComp->opts.disAsm || emitComp->opts.dspEmit || emitComp->verbose)
    {
        emitDispIns(id, false, dspOffs, true, emitCurCodeOffs(odst), *dp, (dst-*dp), ig);
    }

    if (emitComp->compDebugBreak)
    {
        // For example, set JitBreakEmitOutputInstr=a6 will break when this method is called for
        // emitting instruction a6, (i.e. IN00a6 in jitdump).
        static ConfigDWORD fJitBreakEmitOutputInstr;
        if ((unsigned)fJitBreakEmitOutputInstr.val(CLRConfig::INTERNAL_JitBreakEmitOutputInstr) == id->idDebugOnlyInfo()->idNum)
        {
            assert(!"JitBreakEmitOutputInstr reached");
        }
    }
#endif

    /* All instructions are expected to generate code */

    assert(*dp != dst);

    *dp = dst;

    return  sz;
}


/*****************************************************************************/
/*****************************************************************************/

#ifdef DEBUG

/*****************************************************************************
 *
 *  Display the instruction name
 */
void                emitter::emitDispInst(instruction ins)
{
    const char *  insstr = codeGen->genInsName(ins);
    size_t        len    = strlen(insstr);

    /* Display the instruction name */

    printf("%s", insstr);

    //
    // Add at least one space after the instruction name
    // and add spaces until we have reach the normal size of 8
    do {
       printf(" "); 
       len++;
    }
    while (len < 8);
}

/*****************************************************************************
 *
 *  Display an reloc value
 *  If we are formatting for an assembly listing don't print the hex value
 *  since it will prevent us from doing assembly diffs
 */
void                emitter::emitDispReloc(int value, bool addComma)
{
    if (emitComp->opts.disAsm)
    {
        printf("(reloc)");
    }
    else
    {
        printf("(reloc 0x%x)", dspPtr(value));
    }

    if (addComma)
        printf(", ");
}


/*****************************************************************************
 *
 *  Display an immediate value
 */
void                emitter::emitDispImm(ssize_t imm, bool addComma, bool alwaysHex /* =false */)
{
    if (strictArmAsm)
    {
        printf("#");
    }

    // Munge any pointers if we want diff-able disassembly
    if (emitComp->opts.disDiffable)
    {
        ssize_t top44bits = (imm >> 20);
        if ((top44bits != 0) && (top44bits != -1))
            imm = 0xD1FFAB1E;
    }

    if (!alwaysHex && (imm > -1000) && (imm < 1000))
    {
        printf("%d", imm);
    }
    else 
    {
        if ((imm < 0) && ((imm & 0xFFFFFFFF00000000LL) == 0xFFFFFFFF00000000LL))
        {
            printf("-");
            imm = -imm;
        }

        if ((imm & 0xFFFFFFFF00000000LL) != 0)
        {
            printf("0x%llx", imm);
        }
        else
        {
            printf("0x%02x", imm);
        }
    }

    if (addComma)
        printf(", ");
}

/*****************************************************************************
 *
 *  Display a float zero constant
 */
void            emitter::emitDispFloatZero()
{
    if (strictArmAsm)
    {
        printf("#");
    }
    printf("0.0");
}

/*****************************************************************************
 *
 *  Display an encoded float constant value
 */
void            emitter::emitDispFloatImm(ssize_t imm8)
{
    assert((0 <= imm8) && (imm8 <= 0x0ff));
    if (strictArmAsm)
    {
        printf("#");
    }

    floatImm8 fpImm;
    fpImm.immFPIVal = (unsigned) imm8;
    double result = emitDecodeFloatImm8(fpImm);

    printf("%.4f", result);
}

/*****************************************************************************
 *
 *  Display an immediate that is optionally LSL12.
 */
void                emitter::emitDispImmOptsLSL12(ssize_t imm, insOpts opt)
{
    if (!strictArmAsm && insOptsLSL12(opt))
    {
        imm <<= 12;
    }
    emitDispImm(imm, false);
    if (strictArmAsm && insOptsLSL12(opt))
    {
        printf(", LSL #12");
    }
}

/*****************************************************************************
 *
 *  Display an ARM64 condition code for the conditional instructions
 */
void                emitter::emitDispCond(insCond cond)
{
    const static char* armCond[16] = { "eq", "ne", "hs", "lo", 
                                       "mi", "pl", "vs", "vc",
                                       "hi", "ls", "ge", "lt",
                                       "gt", "le", "AL", "NV" };  // The last two are invalid 
    unsigned imm = (unsigned) cond;
    assert((0 <= imm) && (imm < ArrLen(armCond)));
    printf(armCond[imm]);
}

/*****************************************************************************
 *
 *  Display an ARM64 flags for the conditional instructions
 */
void                emitter::emitDispFlags(insCflags flags)
{
    const static char* armFlags[16] = { "0",  "v",   "c",   "cv", 
                                        "z",  "zv",  "zc",  "zcv",
                                        "n",  "nv",  "nc",  "ncv",
                                        "nz", "nzv", "nzc", "nzcv" };
    unsigned imm = (unsigned) flags;
    assert((0 <= imm) && (imm < ArrLen(armFlags)));
    printf(armFlags[imm]);
}

/*****************************************************************************
 *
 *  Display an ARM64 'barrier' for the memory barrier instructions
 */
void                emitter::emitDispBarrier (insBarrier barrier)
{
    const static char* armBarriers[16] = { "#0",  "oshld", "oshst", "osh", 
                                           "#4",  "nshld", "nshst", "nsh",
                                           "#8",  "ishld", "ishst", "ish",
                                           "#12", "ld",    "st",    "sy"  };
    unsigned imm = (unsigned) barrier;
    assert((0 <= imm) && (imm < ArrLen(armBarriers)));
    printf(armBarriers[imm]);
}

/*****************************************************************************
 *
 *  Prints the encoding for the Shift Type encoding
 */

void                emitter::emitDispShiftOpts(insOpts opt)
{
    if (opt == INS_OPTS_LSL)
        printf(" LSL ");
    else if (opt == INS_OPTS_LSR)
        printf(" LSR ");
    else if (opt == INS_OPTS_ASR)
        printf(" ASR ");
    else if (opt == INS_OPTS_ROR)
        printf(" ROR ");
    else if (opt == INS_OPTS_MSL)
        printf(" MSL ");
    else
        assert(!"Bad value");
}

/*****************************************************************************
 *
 *  Prints the encoding for the Extend Type encoding
 */

void                emitter::emitDispExtendOpts(insOpts opt)
{
    if (opt == INS_OPTS_UXTB)
        printf("UXTB");
    else if (opt == INS_OPTS_UXTH)
        printf("UXTH");
    else if (opt == INS_OPTS_UXTW)
        printf("UXTW");
    else if (opt == INS_OPTS_UXTX)
        printf("UXTX");
    else if (opt == INS_OPTS_SXTB)
        printf("SXTB");
    else if (opt == INS_OPTS_SXTH)
        printf("SXTH");
    else if (opt == INS_OPTS_SXTW)
        printf("SXTW");
    else if (opt == INS_OPTS_SXTX)
        printf("SXTX");
    else
        assert(!"Bad value");
}

/*****************************************************************************
 *
 *  Prints the encoding for the Extend Type encoding in loads/stores
 */

void                emitter::emitDispLSExtendOpts(insOpts opt)
{
    if (opt == INS_OPTS_LSL)
        printf("LSL");
    else if (opt == INS_OPTS_UXTW)
        printf("UXTW");
    else if (opt == INS_OPTS_UXTX)
        printf("UXTX");
    else if (opt == INS_OPTS_SXTW)
        printf("SXTW");
    else if (opt == INS_OPTS_SXTX)
        printf("SXTX");
    else
        assert(!"Bad value");
}

/*****************************************************************************
 *
 *  Display a register
 */
void                emitter::emitDispReg(regNumber reg, emitAttr attr, bool addComma)
{
    emitAttr size = EA_SIZE(attr);
    printf(emitRegName(reg, size));

    if (addComma)
        printf(", ");
}

/*****************************************************************************
 *
 *  Display a vector register with an arrangement suffix
 */
void                emitter::emitDispVectorReg(regNumber reg, insOpts opt, bool addComma)
{
    assert(isVectorRegister(reg));
    printf(emitVectorRegName(reg));
    emitDispArrangement(opt);

    if (addComma)
        printf(", ");
}

/*****************************************************************************
 *
 *  Display an vector register index suffix
 */
void                emitter::emitDispVectorRegIndex(regNumber reg, emitAttr elemsize, ssize_t index, bool addComma)
{
    assert(isVectorRegister(reg));
    printf(emitVectorRegName(reg));

    switch (elemsize)
    {
    case EA_1BYTE:
        printf(".b");
        break;
    case EA_2BYTE:
        printf(".h");
        break;
    case EA_4BYTE:
        printf(".s");
        break;
    case EA_8BYTE:
        printf(".d");
        break;
    default:
        assert(!"invalid elemsize");
        break;
    }

    printf("[%d]", index);

    if (addComma)
        printf(", ");
}

/*****************************************************************************
 *
 *  Display an arrangement suffix
 */
void                emitter::emitDispArrangement(insOpts opt)
{
    const char * str = "???";

    switch (opt)
    {
    case INS_OPTS_8B:
        str = "8b";
        break;
    case INS_OPTS_16B:
        str = "16b";
        break;
    case INS_OPTS_4H:
        str = "4h";
        break;
    case INS_OPTS_8H:
        str = "8h";
        break;
    case INS_OPTS_2S:
        str = "2s";
        break;
    case INS_OPTS_4S:
        str = "4s";
        break;
    case INS_OPTS_1D:
        str = "1d";
        break;
    case INS_OPTS_2D:
        str = "2d";
        break;

    default:
        assert(!"Invalid insOpt for vector register");
    }
    printf(".");
    printf(str);
}

/*****************************************************************************
 *
 *  Display a register with an optional shift operation 
 */
void                emitter::emitDispShiftedReg(regNumber reg, insOpts opt, ssize_t imm, emitAttr attr)
{
    emitAttr size = EA_SIZE(attr);
    assert((imm & 0x003F) == imm);
    assert(((imm & 0x0020) == 0) || (size == EA_8BYTE));

    printf(emitRegName(reg, size));

    if (imm > 0)
    {
        if (strictArmAsm)
        {
            printf(",");
        }
        emitDispShiftOpts(opt);
        emitDispImm(imm, false);
    }        
}

/*****************************************************************************
 *
 *  Display a register with an optional extend and scale operations
 */
void                emitter::emitDispExtendReg(regNumber reg, insOpts opt, ssize_t imm)
{
    assert((imm >= 0) && (imm <= 4));
    assert(insOptsNone(opt) || insOptsAnyExtend(opt) || (opt == INS_OPTS_LSL));

    // size is based on the extend option, not the instr size.        
    emitAttr size = insOpts32BitExtend(opt) ? EA_4BYTE : EA_8BYTE;

    if (strictArmAsm)
    {        
        if (insOptsNone(opt))
        {
            emitDispReg(reg, size, false);
        }
        else
        {
            emitDispReg(reg, size, true);
            if (opt == INS_OPTS_LSL)
                printf("LSL");
            else
                emitDispExtendOpts(opt);
            if ((imm > 0) || (opt == INS_OPTS_LSL))
            {
                printf(" ");
                emitDispImm(imm, false);
            }
        }
    }
    else  // !strictArmAsm
    {
        if (insOptsNone(opt))
        {
            emitDispReg(reg, size, false);
        }
        else
        {
            if (opt != INS_OPTS_LSL)
            {
                emitDispExtendOpts(opt);
                printf("(");
                emitDispReg(reg, size, false);
                printf(")");
            }
        }
        if (imm > 0)
        {
            printf("*");
            emitDispImm(1<<imm, false);
        }
    }
}


/*****************************************************************************
 *
 *  Display an addressing operand [reg + imm]
 */
void                emitter::emitDispAddrRI(regNumber reg, insOpts opt, ssize_t imm)
{
    reg = encodingZRtoSP(reg);  // ZR (R31) encodes the SP register

    if (strictArmAsm)
    {
        printf("[");
    
        emitDispReg(reg, EA_8BYTE, false);
    
        if (!insOptsPostIndex(opt) && (imm != 0))
        {
            printf(",");
            emitDispImm(imm, false);
        }
        printf("]");
        
        if (insOptsPreIndex(opt))
        {
            printf("!");
        }
        else if (insOptsPostIndex(opt))
        {
            printf(",");
            emitDispImm(imm, false);
        }
    }
    else  // !strictArmAsm
    {
        printf("[");
    
        const char* operStr  = "++";
        if (imm < 0)
        {
            operStr  = "--";
            imm = -imm;
        }

        if (insOptsPreIndex(opt))
        {
            printf(operStr);
        }

        emitDispReg(reg, EA_8BYTE, false);

        if (insOptsPostIndex(opt))
        {
            printf(operStr);
        }
        
        if (insOptsIndexed(opt))
        {
            printf(", ");
        }
        else
        {
            printf(operStr[1]);
        }
        emitDispImm(imm, false);
        printf("]");
    }    
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg + extended reg]
 */
void                emitter::emitDispAddrRRExt(regNumber reg1,
                                               regNumber reg2, 
                                               insOpts opt,
                                               bool isScaled,
                                               emitAttr size)
{
    reg1 = encodingZRtoSP(reg1);  // ZR (R31) encodes the SP register

    unsigned scale = 0;
    if (isScaled)
    {
        scale = NaturalScale_helper(size);
    }

    printf("[");

    if (strictArmAsm)
    {
        emitDispReg(reg1, EA_8BYTE, true);
        emitDispExtendReg(reg2, opt, scale);
    }
    else  // !strictArmAsm
    {
        emitDispReg(reg1, EA_8BYTE, false);
        printf("+");
        emitDispExtendReg(reg2, opt, scale);
    }

    printf("]");
}

/*****************************************************************************
 *
 *  Display (optionally) the instruction encoding in hex 
 */

void                emitter::emitDispInsHex(BYTE *  code, size_t sz)
{
    // We do not display the instruction hex if we want diff-able disassembly
    if (!emitComp->opts.disDiffable)
    {
        if (sz == 4)
        {
            printf("  %08X    ", (*((code_t *) code)));
        }
    }
}

/****************************************************************************
 *
 *  Display the given instruction.
 */

void                emitter::emitDispIns(instrDesc *  id, 
                                         bool         isNew, 
                                         bool         doffs, 
                                         bool         asmfm,
                                         unsigned     offset,
                                         BYTE *       pCode,
                                         size_t       sz,
                                         insGroup *   ig)
{
    if (EMITVERBOSE)
    {
        unsigned  idNum = id->idDebugOnlyInfo()->idNum;    // Do not remove this!  It is needed for VisualStudio conditional breakpoints

        printf("IN%04x: ", idNum);
    }

    if (pCode == NULL)
        sz = 0;

    if  (!emitComp->opts.dspEmit && !isNew && !asmfm && sz)
        doffs = true;

    /* Display the instruction offset */

    emitDispInsOffs(offset, doffs);

    /* Display the instruction hex code */

    emitDispInsHex(pCode, sz);

    printf("      ");

    /* Get the instruction and format */

    instruction   ins = id->idIns();
    insFormat     fmt = id->idInsFmt();

    emitDispInst(ins);

    /* If this instruction has just been added, check its size */

    assert(isNew == false || (int)emitSizeOfInsDsc(id) == emitCurIGfreeNext - (BYTE*)id);

    /* Figure out the operand size */
    emitAttr  size = id->idOpSize();
    emitAttr  attr = size;
    if       (id->idGCref() == GCT_GCREF)
        attr = EA_GCREF;
    else if  (id->idGCref() == GCT_BYREF)
        attr = EA_BYREF;

    switch (fmt)
    {
        code_t          code;
        ssize_t         imm;
        int             doffs;
        bool            isExtendAlias;
        bool            canEncode;
        bitMaskImm      bmi;
        halfwordImm     hwi;
        condFlagsImm    cfi;
        unsigned        scale;
        unsigned        immShift;
        bool            hasShift;
        ssize_t         offs;
        const char  *   methodName;
        emitAttr        elemsize;
        emitAttr        datasize;
        emitAttr        srcsize;
        emitAttr        dstsize;
        ssize_t         index;
        ssize_t         index2;

    case IF_BI_0A:    // BI_0A   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
    case IF_BI_0B:    // BI_0B   ......iiiiiiiiii iiiiiiiiiii.....               simm19:00
        {
            if (id->idAddr()->iiaHasInstrCount())
            {
                int instrCount = id->idAddr()->iiaGetInstrCount();

                if (ig == nullptr)
                {
                    printf("pc%s%d instructions", (instrCount >= 0) ? "+" : "" , instrCount);
                }
                else
                {
                    unsigned insNum        = emitFindInsNum(ig, id);
                    UNATIVE_OFFSET srcOffs = ig->igOffs + emitFindOffset(ig, insNum + 1);
                    UNATIVE_OFFSET dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount);
                    ssize_t relOffs = (ssize_t) (emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs));            
                    printf("pc%s%d (%d instructions)", (relOffs >= 0) ? "+" : "" , relOffs, instrCount);
                }
            }
            else if  (id->idIsBound())
            {
                printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
            }
            else
            {
                printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
            }
        }
        break;

    case IF_BI_0C:    // BI_0C   ......iiiiiiiiii iiiiiiiiiiiiiiii               simm26:00
        if (id->idIsCallAddr())
        {
            offs = (ssize_t)id->idAddr()->iiaAddr;
            methodName = "";
        }
        else
        {
            offs = 0;
            methodName = emitComp->eeGetMethodFullName((CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie);
        }

        if (offs)
        {
            if (id->idIsDspReloc())
                printf("reloc ");
            printf("%08X", offs);
        }
        else
        {
            printf("%s", methodName);
        }
        break;

    case IF_BI_1A:    // BI_1A   ......iiiiiiiiii iiiiiiiiiiittttt      Rt       simm19:00
        assert(insOptsNone(id->idInsOpt()));
        emitDispReg(id->idReg1(), size, true);
        if (id->idIsBound())
        {
            printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
        }
        else
        {
            printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
        }
        break;

    case IF_BI_1B:    // BI_1B   B.......bbbbbiii iiiiiiiiiiittttt      Rt imm6, simm14:00
        assert(insOptsNone(id->idInsOpt()));
        emitDispReg(id->idReg1(), size, true);
        emitDispImm(emitGetInsSC(id), true);
        if (id->idIsBound())
        {
            printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
        }
        else
        {
            printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
        }
        break;

    case IF_BR_1A:    // BR_1A   ................ ......nnnnn.....         Rn
        assert(insOptsNone(id->idInsOpt()));
        emitDispReg(id->idReg1(), size, false);
        break;

    case IF_LS_1A:    // LS_1A   XX......iiiiiiii iiiiiiiiiiittttt      Rt    PC imm(1MB)
        assert(insOptsNone(id->idInsOpt()));
        emitDispReg(id->idReg1(), size, true);
        imm = emitGetInsSC(id);

        /* Is this actually a reference to a data section? */
        doffs = Compiler::eeGetJitDataOffs(id->idAddr()->iiaFieldHnd);

        printf("[");
        if  (doffs >= 0)
        {
            /* Display a data section reference */

            if  (doffs & 1)
                printf("@CNS%02u", doffs-1);
            else
                printf("@RWD%02u", doffs);

            if  (imm != 0)
                printf("%+Id", imm);
        }
        else
        {
            assert(imm == 0);
            if (id->idIsBound())
            {
                printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
            }
            else
            {
                printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
            }
        }
        printf("]");
        break;

    case IF_LS_2A:    // LS_2A   .X.......X...... ......nnnnnttttt      Rt Rn
        assert(insOptsNone(id->idInsOpt()));
        assert(emitGetInsSC(id) == 0);
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        emitDispAddrRI(id->idReg2(), id->idInsOpt(), 0);
        break;

    case IF_LS_2B:    // LS_2B   .X.......Xiiiiii iiiiiinnnnnttttt      Rt Rn    imm(0-4095)
        assert(insOptsNone(id->idInsOpt()));
        imm = emitGetInsSC(id);
        scale = NaturalScale_helper(emitInsLoadStoreSize(id));
        imm <<= scale;    // The immediate is scaled by the size of the ld/st
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        emitDispAddrRI(id->idReg2(), id->idInsOpt(), imm);
        break;

    case IF_LS_2C:    // LS_2C   .X.......X.iiiii iiiiPPnnnnnttttt      Rt Rn    imm(-256..+255) no/pre/post inc
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        imm = emitGetInsSC(id);
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        emitDispAddrRI(id->idReg2(), id->idInsOpt(), imm);
        break;

    case IF_LS_3A:    // LS_3A   .X.......X.mmmmm oooS..nnnnnttttt      Rt Rn Rm ext(Rm) LSL {}
        assert(insOptsLSExtend(id->idInsOpt()));
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        if (id->idIsLclVar())
        {
            emitDispAddrRRExt(id->idReg2(), codeGen->rsGetRsvdReg(), id->idInsOpt(), false, size);
        }
        else
        {
            emitDispAddrRRExt(id->idReg2(), id->idReg3(), id->idInsOpt(), id->idReg3Scaled(), size);
        }
        break;

    case IF_LS_3B:    // LS_3B   X............... .aaaaannnnnddddd      Rt Ra Rn
        assert(insOptsNone(id->idInsOpt()));
        assert(emitGetInsSC(id) == 0);
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        emitDispReg(id->idReg2(), emitInsTargetRegSize(id), true);
        emitDispAddrRI(id->idReg3(), id->idInsOpt(), 0);
        break;

    case IF_LS_3C:    // LS_3C   X.........iiiiii iaaaaannnnnddddd      Rt Ra Rn imm(im7,sh)
        assert(insOptsNone(id->idInsOpt()) || insOptsIndexed(id->idInsOpt()));
        imm = emitGetInsSC(id);
        scale = NaturalScale_helper(emitInsLoadStoreSize(id));
        imm <<= scale;
        emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true);
        emitDispReg(id->idReg2(), emitInsTargetRegSize(id), true);
        emitDispAddrRI(id->idReg3(), id->idInsOpt(), imm);
        break;

    case IF_DI_1A:    // DI_1A   X.......shiiiiii iiiiiinnnnn.....      Rn       imm(i12,sh)
        emitDispReg(id->idReg1(), size, true);
        emitDispImmOptsLSL12(emitGetInsSC(id), id->idInsOpt());
        break;

    case IF_DI_1B:    // DI_1B   X........hwiiiii iiiiiiiiiiiddddd      Rd       imm(i16,hw)
        emitDispReg(id->idReg1(), size, true);
        hwi.immHWVal = (unsigned) emitGetInsSC(id);
        if (ins == INS_mov)
        {
            emitDispImm(emitDecodeHalfwordImm(hwi, size), false);
        }
        else  // movz, movn, movk
        {
            emitDispImm(hwi.immVal, false);
            if (hwi.immHW != 0)
            {
                emitDispShiftOpts(INS_OPTS_LSL);
                emitDispImm(hwi.immHW * 16, false);
            }
        }
        break;

    case IF_DI_1C:    // DI_1C   X........Nrrrrrr ssssssnnnnn.....         Rn    imm(N,r,s)
        emitDispReg(id->idReg1(), size, true);
        bmi.immNRS = (unsigned) emitGetInsSC(id);
        emitDispImm(emitDecodeBitMaskImm(bmi, size), false);
        break;

    case IF_DI_1D:    // DI_1D   X........Nrrrrrr ssssss.....ddddd      Rd       imm(N,r,s)
        emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
        bmi.immNRS = (unsigned) emitGetInsSC(id);
        emitDispImm(emitDecodeBitMaskImm(bmi, size), false);
        break;

    case IF_DI_1E:    // DI_1E   .ii.....iiiiiiii iiiiiiiiiiiddddd      Rd       simm21
        assert(insOptsNone(id->idInsOpt()));
        emitDispReg(id->idReg1(), size, true);
        if (id->idIsBound())
        {
            printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
        }
        else
        {
            printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
        }
        break;

    case IF_DI_2A:    // DI_2A   X.......shiiiiii iiiiiinnnnnddddd      Rd Rn    imm(i12,sh)
        if ((ins == INS_add) || (ins == INS_sub))
        {
            emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
            emitDispReg(encodingZRtoSP(id->idReg2()), size, true);
        }
        else
        {
            emitDispReg(id->idReg1(), size, true);
            emitDispReg(id->idReg2(), size, true);
        }
        emitDispImmOptsLSL12(emitGetInsSC(id), id->idInsOpt());
        break;

    case IF_DI_2B:    // DI_2B   X........X.nnnnn ssssssnnnnnddddd      Rd Rn    imm(0-63)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispImm(emitGetInsSC(id), false);
        break;

    case IF_DI_2C:    // DI_2C   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imm(N,r,s)
        if (ins == INS_ands)
        {
            emitDispReg(id->idReg1(), size, true);
        }
        else
        {
            emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
        }
        emitDispReg(id->idReg2(), size, true);
        bmi.immNRS = (unsigned) emitGetInsSC(id);
        emitDispImm(emitDecodeBitMaskImm(bmi, size), false);
        break;

    case IF_DI_2D:    // DI_2D   X........Nrrrrrr ssssssnnnnnddddd      Rd Rn    imr, ims   (N,r,s)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);

        imm = emitGetInsSC(id);
        bmi.immNRS = (unsigned)imm;

        switch (ins)
        {
        case INS_bfm:
        case INS_sbfm:
        case INS_ubfm:
            emitDispImm(bmi.immR, true);
            emitDispImm(bmi.immS, false);
            break;

        case INS_bfi:
        case INS_sbfiz:
        case INS_ubfiz:
            emitDispImm(getBitWidth(size) - bmi.immR, true);
            emitDispImm(bmi.immS + 1, false);
            break;

        case INS_bfxil:
        case INS_sbfx:
        case INS_ubfx:
            emitDispImm(bmi.immR, true);
            emitDispImm(bmi.immS - bmi.immR + 1, false);
            break;

        case INS_asr:
        case INS_lsr:
        case INS_lsl:
            emitDispImm(imm, false);
            break;

        default:
            assert(!"Unexpected instruction in IF_DI_2D");
        }

        break;

    case IF_DI_1F:    // DI_1F   X..........iiiii cccc..nnnnn.nzcv      Rn imm5  nzcv cond
        emitDispReg(id->idReg1(), size, true);
        cfi.immCFVal = (unsigned) emitGetInsSC(id);
        emitDispImm(cfi.imm5, true);
        emitDispFlags(cfi.flags);
        printf(",");
        emitDispCond(cfi.cond);
        break;

    case IF_DR_1D:    // DR_1D   X............... cccc.......mmmmm      Rd       cond
        emitDispReg(id->idReg1(), size, true);
        cfi.immCFVal = (unsigned) emitGetInsSC(id);
        emitDispCond(cfi.cond);
        break;

    case IF_DR_2A:    // DR_2A   X..........mmmmm ......nnnnn.....         Rn Rm
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, false);
        break;

    case IF_DR_2B:    // DR_2B   X.......sh.mmmmm ssssssnnnnn.....         Rn Rm {LSL,LSR,ASR,ROR} imm(0-63)
        emitDispReg(id->idReg1(), size, true);
        emitDispShiftedReg(id->idReg2(), id->idInsOpt(), emitGetInsSC(id), size);
        break;

    case IF_DR_2C:    // DR_2C   X..........mmmmm ooosssnnnnn.....         Rn Rm ext(Rm) LSL imm(0-4)
        emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
        imm = emitGetInsSC(id);
        emitDispExtendReg(id->idReg2(), id->idInsOpt(), imm);
        break;

    case IF_DR_2D:    // DR_2D   X..........nnnnn cccc..nnnnnddddd      Rd Rn    cond
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        cfi.immCFVal = (unsigned) emitGetInsSC(id);
        emitDispCond(cfi.cond);
        break;

    case IF_DR_2E:    // DR_2E   X..........mmmmm ...........ddddd      Rd    Rm
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, false);
        break;

    case IF_DR_2F:    // DR_2F   X.......sh.mmmmm ssssss.....ddddd      Rd    Rm {LSL,LSR,ASR} imm(0-63)
        emitDispReg(id->idReg1(), size, true);
        emitDispShiftedReg(id->idReg2(), id->idInsOpt(), emitGetInsSC(id), size);
        break;

    case IF_DR_2G:    // DR_2G   X............... ......nnnnnddddd      Rd Rn
        emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
        emitDispReg(encodingZRtoSP(id->idReg2()), size, false);
        break;

    case IF_DR_2H:    // DR_2H   X........X...... ......nnnnnddddd      Rd Rn
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, false);
        break;

    case IF_DR_2I:    // DR_2I   X..........mmmmm cccc..nnnnn.nzcv      Rn Rm    nzcv cond
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        cfi.immCFVal = (unsigned) emitGetInsSC(id);
        emitDispFlags(cfi.flags);
        printf(",");
        emitDispCond(cfi.cond);
        break;

    case IF_DR_3A:    // DR_3A   X..........mmmmm ......nnnnnmmmmm      Rd Rn Rm
        if ((ins == INS_add) || (ins == INS_sub) )
        {
            emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
            emitDispReg(encodingZRtoSP(id->idReg2()), size, true);
        }
        else
        {
            emitDispReg(id->idReg1(), size, true);
            emitDispReg(id->idReg2(), size, true);
        }
        if (id->idIsLclVar())
        {
            emitDispReg(codeGen->rsGetRsvdReg(), size, false);
        }
        else
        {
            emitDispReg(id->idReg3(), size, false);
        }
        
        break;

    case IF_DR_3B:    // DR_3B   X.......sh.mmmmm ssssssnnnnnddddd      Rd Rn Rm {LSL,LSR,ASR} imm(0-63)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispShiftedReg(id->idReg3(), id->idInsOpt(), emitGetInsSC(id), size);
        break;

    case IF_DR_3C:    // DR_3C   X..........mmmmm ooosssnnnnnddddd      Rd Rn Rm ext(Rm) LSL imm(0-4)
        emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
        emitDispReg(encodingZRtoSP(id->idReg2()), size, true);
        imm = emitGetInsSC(id);
        emitDispExtendReg(id->idReg3(), id->idInsOpt(), imm);
        break;

    case IF_DR_3D:    // DR_3D   X..........mmmmm cccc..nnnnnmmmmm      Rd Rn Rm cond
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispReg(id->idReg3(), size, true);
        cfi.immCFVal = (unsigned) emitGetInsSC(id);
        emitDispCond(cfi.cond);
        break;

    case IF_DR_3E:    // DR_3E   X........X.mmmmm ssssssnnnnnddddd      Rd Rn Rm imm(0-63)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispReg(id->idReg3(), size, true);
        emitDispImm(emitGetInsSC(id), false);            
       break;

    case IF_DR_4A:    // DR_4A   X..........mmmmm .aaaaannnnnmmmmm      Rd Rn Rm Ra
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispReg(id->idReg3(), size, true);
        emitDispReg(id->idReg4(), size, false);
        break;

    case IF_DV_1A:    // DV_1A   .........X.iiiii iii........ddddd      Vd imm8 (fmov - immediate scalar)
        elemsize = id->idOpSize();
        emitDispReg(id->idReg1(), elemsize, true);
        emitDispFloatImm(emitGetInsSC(id));
        break;

    case IF_DV_1B:    // DV_1B   .QX..........iii cmod..iiiiiddddd      Vd imm8 (immediate vector)
        imm      = emitGetInsSC(id) & 0x0ff;
        immShift = (emitGetInsSC(id) & 0x700) >> 8;
        hasShift = (immShift != 0);
        elemsize = optGetElemsize(id->idInsOpt());
        if (id->idInsOpt() == INS_OPTS_1D)
        {
             assert(elemsize == size);
             emitDispReg(id->idReg1(), size, true);
        }
        else
        {
            emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        }
        if (ins == INS_fmov) 
        {
            emitDispFloatImm(imm);
            assert(hasShift == false);
        }
        else
        {
            if (elemsize == EA_8BYTE)
            {
                assert(ins == INS_movi);
                ssize_t       imm64 = 0;
                const ssize_t mask8 = 0xFF;
                for (unsigned b=0; b<8; b++)
                {
                    if (imm & (1<<b))
                    {
                        imm64 |= (mask8 << (b*8));
                    }
                }
                emitDispImm(imm64, hasShift, true);
            }
            else
            {
                emitDispImm(imm, hasShift, true);
            }
            if (hasShift)
            {
                insOpts  opt   = (immShift & 0x4) ? INS_OPTS_MSL : INS_OPTS_LSL;
                unsigned shift = (immShift & 0x3) * 8;
                emitDispShiftOpts(opt);
                emitDispImm(shift, false);
            }
        }
        break;

    case IF_DV_1C:    // DV_1C   .........X...... ......nnnnn.....      Vn #0.0 (fcmp - with zero)
        elemsize = id->idOpSize();
        emitDispReg(id->idReg1(), elemsize, true);
        emitDispFloatZero();
        break;

    case IF_DV_2A:    // DV_2A   .Q.......X...... ......nnnnnddddd      Vd Vn   (fabs, fcvt - vector)
    case IF_DV_2M:    // DV_2M   .Q......XX...... ......nnnnnddddd      Vd Vn   (abs, neg   - vector)
        emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        emitDispVectorReg(id->idReg2(), id->idInsOpt(), false);
        break;

    case IF_DV_2N:    // DV_2N   .........iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - scalar)
        elemsize = id->idOpSize();
        emitDispReg(id->idReg1(), elemsize, true);
        emitDispReg(id->idReg2(), elemsize, true);
        emitDispImm(emitGetInsSC(id), false);
        break;

    case IF_DV_2O:    // DV_2O   .Q.......iiiiiii ......nnnnnddddd      Vd Vn imm   (shift - vector)
        imm = emitGetInsSC(id);
        // Do we have a sxtl or uxtl instruction?
        isExtendAlias = ((ins == INS_sxtl) || (ins == INS_sxtl2) || (ins == INS_uxtl) || (ins == INS_uxtl2));
        code = emitInsCode(ins, fmt);
        if (code & 0x00008000)      // widen/narrow opcodes
        {
            if (code & 0x00002000)  // SHL opcodes
            {
                emitDispVectorReg(id->idReg1(), optWidenElemsize(id->idInsOpt()), true);
                emitDispVectorReg(id->idReg2(), id->idInsOpt(), !isExtendAlias);
            }
            else                    // SHR opcodes
            {
                emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
                emitDispVectorReg(id->idReg2(), optWidenElemsize(id->idInsOpt()), !isExtendAlias);
            }
       }
        else
        {
            emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
            emitDispVectorReg(id->idReg2(), id->idInsOpt(), !isExtendAlias);
        }
        // Print the immediate unless we have a sxtl or uxtl instruction
        if (!isExtendAlias)
        {
            emitDispImm(imm, false);
        }
        break;

    case IF_DV_2B:    // DV_2B   .Q.........iiiii ......nnnnnddddd      Rd Vn[] (umov/smov    - to general)
        srcsize = id->idOpSize();
        index = emitGetInsSC(id);       
        if (ins == INS_smov)
        {
            dstsize = EA_8BYTE;
        }
        else // INS_umov or INS_mov
        {
            dstsize = (srcsize == EA_8BYTE) ? EA_8BYTE : EA_4BYTE;
        }
        emitDispReg(id->idReg1(), dstsize, true);
        emitDispVectorRegIndex(id->idReg2(), srcsize, index, false);
        break;

    case IF_DV_2C:    // DV_2C   .Q.........iiiii ......nnnnnddddd      Vd Rn   (dup/ins - vector from general)
        if (ins == INS_dup) 
        {
            datasize = id->idOpSize();
            assert(isValidVectorDatasize(datasize));
            assert(isValidArrangement(datasize, id->idInsOpt()));
            elemsize = optGetElemsize(id->idInsOpt());
            emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        }
        else  // INS_ins
        {
            elemsize = id->idOpSize();
            index = emitGetInsSC(id);
            assert(isValidVectorElemsize(elemsize));
            emitDispVectorRegIndex(id->idReg1(), elemsize, index, true);
        }
        emitDispReg(id->idReg2(), (elemsize == EA_8BYTE) ? EA_8BYTE : EA_4BYTE, false);
        break;

    case IF_DV_2D:    // DV_2D   .Q.........iiiii ......nnnnnddddd      Vd Vn[]   (dup - vector)
        datasize = id->idOpSize();
        assert(isValidVectorDatasize(datasize));
        assert(isValidArrangement(datasize, id->idInsOpt()));
        elemsize = optGetElemsize(id->idInsOpt());
        index = emitGetInsSC(id);
        emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        emitDispVectorRegIndex(id->idReg2(), elemsize, index, false);
        break;

    case IF_DV_2E:    // DV_2E   ...........iiiii ......nnnnnddddd      Vd Vn[]   (dup - scalar)
        elemsize = id->idOpSize();
        index = emitGetInsSC(id);
        emitDispReg(id->idReg1(), elemsize, true);
        emitDispVectorRegIndex(id->idReg2(), elemsize, index, false);
        break;

    case IF_DV_2F:    // DV_2F   ...........iiiii .jjjj.nnnnnddddd      Vd[] Vn[] (ins - element)
        imm = emitGetInsSC(id);
        index  = (imm >> 4) & 0xf;
        index2 = imm & 0xf;
        elemsize = id->idOpSize();
        emitDispVectorRegIndex(id->idReg1(), elemsize, index, true);
        emitDispVectorRegIndex(id->idReg2(), elemsize, index2, false);
        break;

    case IF_DV_2G:    // DV_2G   .........X...... ......nnnnnddddd      Vd Vn      (fmov, fcvtXX - register)
    case IF_DV_2K:    // DV_2K   .........X.mmmmm ......nnnnn.....      Vn Vm      (fcmp)
    case IF_DV_2L:    // DV_2L   ........XX...... ......nnnnnddddd      Vd Vn      (abs, neg - scalar)
        elemsize = id->idOpSize();
        emitDispReg(id->idReg1(), elemsize, true);
        emitDispReg(id->idReg2(), elemsize, false);
        break;

    case IF_DV_2H:    // DV_2H   X........X...... ......nnnnnddddd      Rd Vn      (fmov, fcvtXX - to general)
    case IF_DV_2I:    // DV_2I   X........X...... ......nnnnnddddd      Vd Rn      (fmov, Xcvtf - from general)
    case IF_DV_2J:    // DV_2J   ........SS.....D D.....nnnnnddddd      Vd Vn      (fcvt)
        dstsize = optGetDstsize(id->idInsOpt());
        srcsize = optGetSrcsize(id->idInsOpt());
  
        emitDispReg(id->idReg1(), dstsize, true);
        emitDispReg(id->idReg2(), srcsize, false);
        break;

    case IF_DV_3A:    // DV_3A   .Q......XX.mmmmm ......nnnnnddddd      Vd Vn Vm  (vector)
    case IF_DV_3B:    // DV_3B   .Q.........mmmmm ......nnnnnddddd      Vd Vn Vm  (vector)
        emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        emitDispVectorReg(id->idReg2(), id->idInsOpt(), true);
        emitDispVectorReg(id->idReg3(), id->idInsOpt(), false);
        break;

    case IF_DV_3C:    // DV_3C   .Q.........mmmmm ......nnnnnddddd      Vd Vn Vm  (vector)
        emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        if (ins != INS_mov)
        {
            emitDispVectorReg(id->idReg2(), id->idInsOpt(), true);
        }
        emitDispVectorReg(id->idReg3(), id->idInsOpt(), false);
        break;

    case IF_DV_3AI:   // DV_3AI  .Q......XXLMmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
    case IF_DV_3BI:   // DV_3BI  .Q........Lmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (vector by elem)
        emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
        emitDispVectorReg(id->idReg2(), id->idInsOpt(), true);
        elemsize = optGetElemsize(id->idInsOpt());
        emitDispVectorRegIndex(id->idReg3(), elemsize, emitGetInsSC(id), false);
        break;

    case IF_DV_3D:    // DV_3D   .........X.mmmmm ......nnnnnddddd      Vd Vn Vm  (scalar)
    case IF_DV_3E:    // DV_3E   ...........mmmmm ......nnnnnddddd      Vd Vn Vm  (scalar)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispReg(id->idReg3(), size, false);
        break;

    case IF_DV_3DI:   // DV_3DI  .........XLmmmmm ....H.nnnnnddddd      Vd Vn Vm[] (scalar by elem)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        elemsize = size;
        emitDispVectorRegIndex(id->idReg3(), elemsize, emitGetInsSC(id), false);
        break;

    case IF_DV_4A:    // DV_4A   .........X.mmmmm .aaaaannnnnddddd      Vd Va Vn Vm (scalar)
        emitDispReg(id->idReg1(), size, true);
        emitDispReg(id->idReg2(), size, true);
        emitDispReg(id->idReg3(), size, true);
        emitDispReg(id->idReg4(), size, false);
        break;

    case IF_SN_0A:    // SN_0A   ................ ................      
        break;

    case IF_SI_0A:    // SI_0A   ...........iiiii iiiiiiiiiii.....               imm16
        emitDispImm(emitGetInsSC(id), false);
        break;

    case IF_SI_0B:    // SI_0B   ................ ....bbbb........               imm4 - barrier
        emitDispBarrier((insBarrier)emitGetInsSC(id));
        break;

    default:
        printf("unexpected format %s", emitIfName(id->idInsFmt()));
        assert(!"unexpectedFormat");
        break;
    }

    if (id->idDebugOnlyInfo()->idVarRefOffs)
    {
        printf("\t// ");
        emitDispFrameRef(id->idAddr()->iiaLclVar.lvaVarNum(),
                         id->idAddr()->iiaLclVar.lvaOffset(),
                         id->idDebugOnlyInfo()->idVarRefOffs, asmfm);
    }

    printf("\n");
}

/*****************************************************************************
 *
 *  Display a stack frame reference.
 */

void                emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
{
    printf("[");

        if  (varx < 0)
            printf("TEMP_%02u", -varx);
        else
            emitComp->gtDispLclVar(+varx, false);

        if (disp < 0)
                printf("-0x%02x", -disp);
        else if (disp > 0)
                printf("+0x%02x", +disp);

    printf("]");

    if  (varx >= 0 && emitComp->opts.varNames)
    {
        LclVarDsc*  varDsc;
        const   char *        varName;

        assert((unsigned)varx < emitComp->lvaCount);
        varDsc  = emitComp->lvaTable + varx;
        varName = emitComp->compLocalVarName(varx, offs);

        if  (varName)
        {
            printf("'%s", varName);

            if      (disp < 0)
                    printf("-%d", -disp);
            else if (disp > 0)
                    printf("+%d", +disp);

            printf("'");
        }
    }
}


#endif // DEBUG

// this is very similar to emitInsBinary and probably could be folded in to same
// except the requirements on the incoming parameter are different,
// ex: the memory op in storeind case must NOT be contained
void emitter::emitInsMov(instruction ins, emitAttr attr, GenTree* node)
{
    switch (node->OperGet())
    {
    case GT_IND:
        {
            assert(emitInsIsLoad(ins));

            GenTreeIndir* indir = node->AsIndir();
            GenTree*      addr  = node->gtGetOp1();

            // The value of 'ins' encodes the size to load 
            // we use EA_8BYTE here because it is the size we want to write (into node->gtRegNum)
            // it is also required when ins is INS_ldrsw
            //
            if (EA_SIZE(attr) < EA_8BYTE)
            {
                attr = EA_8BYTE;
            }

            if (addr->isContained())
            {
                assert(addr->OperGet() == GT_LCL_VAR ||
                       addr->OperGet() == GT_LEA);

                int offset = 0;

                if (addr->OperGet() == GT_LEA)
                {
                    offset = (int) addr->AsAddrMode()->gtOffset;
                }
                GenTree*  memBase = indir->Base();

                if (indir->HasIndex())
                {
                    assert(offset == 0);
                    GenTree*  index = indir->Index();

                    emitIns_R_R_R(ins, attr, node->gtRegNum, 
                                  memBase->gtRegNum, index->gtRegNum);
                }
                else
                {
                    emitIns_R_R_I(ins, attr, node->gtRegNum, 
                                  memBase->gtRegNum, offset);
                }
            }
            else
            {
                codeGen->genConsumeReg(addr);
                emitIns_R_R(ins, attr, node->gtRegNum, addr->gtRegNum);
            }
        }
        break;

    case GT_STOREIND:
        {
            assert(emitInsIsStore(ins));

            GenTreeIndir* indir   = node->AsIndir();
            GenTree*      addr    = node->gtGetOp1();
            GenTree*      data    = node->gtGetOp2();
            regNumber     dataReg = REG_NA;
            if (data->isContainedIntOrIImmed())
            {
                assert(data->IsZero());
                dataReg = REG_ZR;
            }
            else
            {
                assert(!data->isContained());
                codeGen->genConsumeReg(data);
                dataReg = data->gtRegNum;
            }

            // The value of 'ins' encodes the size to store 
            // we use EA_4BYTE here because it is the size of the register
            // that we want to display when storing small values 
            //
            if (EA_SIZE(attr) < EA_4BYTE)
            {
                attr = EA_4BYTE;
            }

            if (addr->isContained())
            {
                assert(addr->OperGet() == GT_LCL_VAR_ADDR ||
                       addr->OperGet() == GT_LEA);

                int offset = 0;

                if (addr->OperGet() == GT_LEA)
                {
                    offset = (int) addr->AsAddrMode()->gtOffset;
                }
                GenTree*  memBase = indir->Base();

                if (indir->HasIndex())
                {
                    assert(offset == 0);
                    GenTree*  index = indir->Index();

                    emitIns_R_R_R(ins, attr, dataReg, 
                                  memBase->gtRegNum, index->gtRegNum);
                }
                else
                {
                    emitIns_R_R_I(ins, attr, dataReg, 
                                  memBase->gtRegNum, offset);
                }
            }
            else
            {
                codeGen->genConsumeReg(addr);
                emitIns_R_R(ins, attr, dataReg, addr->gtRegNum);
            }
        }
        break;

    case GT_STORE_LCL_VAR:
        {
            assert(emitInsIsStore(ins));

            GenTreeLclVarCommon* varNode = node->AsLclVarCommon();

            GenTree*  data    = node->gtOp.gtOp1->gtEffectiveVal();
            regNumber dataReg = REG_NA;
            if (data->isContainedIntOrIImmed())
            {
                assert(data->IsZero());
                dataReg = REG_ZR;
            }
            else
            {
                assert(!data->isContained());
                codeGen->genConsumeReg(data);
                dataReg = data->gtRegNum;
            }

            codeGen->inst_set_SV_var(varNode);
            assert(varNode->gtRegNum == REG_NA); // stack store

            emitIns_S_R(ins, attr, dataReg, 
                                   varNode->GetLclNum(), 0);

            codeGen->genUpdateLife(varNode);
        }
        return;

    default:
        unreached();
    }
}

// Generates a float or double data section constant and returns field handle representing
// the data offset to access the constant.  This is called by emitInsBinary() in case
// of contained float of double constants.
CORINFO_FIELD_HANDLE emitter::emitFltOrDblConst(GenTreeDblCon* tree, emitAttr attr /*=EA_UNKNOWN*/)
{
    if (attr == EA_UNKNOWN)
    {
        attr = emitTypeSize(tree->TypeGet());
    }
    else
    {
        assert(emitTypeSize(tree->TypeGet()) == attr);    
    }

    double constValue = tree->gtDblCon.gtDconVal;
    void *cnsAddr;
    float f;
    bool dblAlign;

    if (attr == EA_4BYTE)
    {        
        f  = forceCastToFloat(constValue);
        cnsAddr = &f;
        dblAlign = false;
    }
    else
    {
        cnsAddr = &constValue;
        dblAlign = true;
    }

    // Access to inline data is 'abstracted' by a special type of static member
    // (produced by eeFindJitDataOffs) which the emitter recognizes as being a reference
    // to constant data, not a real static field.

    UNATIVE_OFFSET cnsSize = (attr == EA_4BYTE) ? 4 : 8;
    UNATIVE_OFFSET cnum = emitDataConst(cnsAddr, cnsSize, dblAlign);
    return emitComp->eeFindJitDataOffs(cnum);
}

// The callee must call genConsumeReg() for any non-contained srcs
// and genProduceReg() for any non-contained dsts.

regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src)
{
    regNumber result = REG_NA;

    // dst can only be a reg
    assert(!dst->isContained());

    // src can be immed or reg
    assert(!src->isContained() || src->isContainedIntOrIImmed());

    // find immed (if any) - it cannot be a dst
    GenTreeIntConCommon* intConst = nullptr;
    if (src->isContainedIntOrIImmed())
    {
        intConst = src->AsIntConCommon();
    }

    if (intConst)
    {
        emitIns_R_I(ins, attr, dst->gtRegNum, intConst->IconValue());
        return dst->gtRegNum;
    }
    else
    {
        emitIns_R_R(ins, attr, dst->gtRegNum, src->gtRegNum);
        return dst->gtRegNum;
    }
}

// The callee must call genConsumeReg() for any non-contained srcs
// and genProduceReg() for any non-contained dsts.

regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src1, GenTree* src2)
{
    regNumber result = REG_NA;

    // dst can only be a reg
    assert(!dst->isContained());

    // find immed (if any) - it cannot be a dst
    // Only one src can be an int.
    GenTreeIntConCommon* intConst = nullptr;
    GenTree* nonIntReg = nullptr;
    
    if (varTypeIsFloating(dst))
    {
        // src1 can only be a reg
        assert(!src1->isContained());
        // src2 can only be a reg
        assert(!src2->isContained());
    }
    else // not floating point
    {
        // src2 can be immed or reg
        assert(!src2->isContained() || src2->isContainedIntOrIImmed());

        // Check src2 first as we can always allow it to be a contained immediate
        if (src2->isContainedIntOrIImmed())
        {
            intConst = src2->AsIntConCommon();
            nonIntReg = src1;
        }
        // Only for commutative operations do we check src1 and allow it to be a contained immediate
        else if (dst->OperIsCommutative())
        {
            // src1 can be immed or reg
            assert(!src1->isContained() || src1->isContainedIntOrIImmed());

            // Check src1 and allow it to be a contained immediate
            if (src1->isContainedIntOrIImmed())
            {
                assert(!src2->isContainedIntOrIImmed());
                intConst = src1->AsIntConCommon();
                nonIntReg = src2;
            }
        }
        else
        {
            // src1 can only be a reg
            assert(!src1->isContained());
        }
    }
    bool isMulOverflow = false;
    bool isUnsignedMul = false;
    instruction ins2 = INS_invalid;
    regNumber extraReg = REG_ZR;
    if (dst->gtOverflowEx())
    {
        if (ins == INS_add)
        {
            ins = INS_adds;
        }
        else if (ins == INS_sub)
        {
            ins = INS_subs;
        }
        else if (ins == INS_mul)
        {
            isMulOverflow = true;
            isUnsignedMul = ((dst->gtFlags & GTF_UNSIGNED) != 0);
            regMaskTP   tmpRegsMask = dst->gtRsvdRegs;
            assert(genCountBits(tmpRegsMask) >= 1);
            regMaskTP extraRegMask = genFindLowestBit(tmpRegsMask);
            tmpRegsMask &= ~extraRegMask;
            extraReg = genRegNumFromMask(extraRegMask);
            ins2 = isUnsignedMul ? INS_umulh : INS_smulh;
            assert(intConst == nullptr);   // overflow format doesn't support an int constant operand
        }
        else
        {
            assert(!"Invalid ins for overflow check");
        }
    }
    if (intConst != nullptr)
    {
        emitIns_R_R_I(ins, attr, dst->gtRegNum, nonIntReg->gtRegNum, intConst->IconValue());
    }
    else
    {
        emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
        if (isMulOverflow)
        {
            emitIns_R_R_R(ins2, attr, extraReg, src1->gtRegNum, src2->gtRegNum);
            if (isUnsignedMul)
            {
                emitIns_R_I(INS_cmp, EA_8BYTE, extraReg, 0);
            }
            else
            {
                emitIns_R_R_I(INS_cmp, EA_8BYTE, extraReg, dst->gtRegNum, 63, INS_OPTS_ASR);
            }
        }
    }

    if (dst->gtOverflowEx())
    {
        assert(!varTypeIsFloating(dst));
        codeGen->genCheckOverflow(dst);
    }

    return dst->gtRegNum;
}

#endif // defined(_TARGET_ARM64_)
